prompt
Gemini said
This is a complete, single-file Java solution using Swing and Graphics2D. It utilizes Path2D, Ellipse2D, and AffineTransform for the geometry and animations.
To run this, simply create a class named SubmarineGame in NetBeans and paste the code below.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
public class SubmarineGame extends JFrame {
public SubmarineGame() {
setTitle("Deep Sea Strike");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new GamePanel());
pack();
setLocationRelativeTo(null);
setResizable(false);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new SubmarineGame().setVisible(true));
}
}
class GamePanel extends JPanel implements ActionListener, KeyListener {
private final int WIDTH = 800;
private final int HEIGHT = 600;
private Timer timer;
private Player player;
private ArrayList<Enemy> enemies;
private ArrayList<Missile> missiles;
private boolean gameOver = false;
private Random rand = new Random();
private long lastEnemyFire = 0;
public GamePanel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(new Color(0, 50, 100));
setFocusable(true);
addKeyListener(this);
initGame();
timer = new Timer(16, this); // ~60 FPS
timer.start();
}
private void initGame() {
player = new Player(100, 300);
enemies = new ArrayList<>();
missiles = new ArrayList<>();
for (int i = 0; i < 20; i++) {
enemies.add(new Enemy(400 + rand.nextInt(350), rand.nextInt(550)));
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (gameOver) {
g2d.setColor(Color.WHITE);
g2d.setFont(new Font("Arial", Font.BOLD, 50));
g2d.drawString("GAME OVER", WIDTH / 2 - 150, HEIGHT / 2);
g2d.setFont(new Font("Arial", Font.PLAIN, 20));
g2d.drawString("Press 'R' to Restart", WIDTH / 2 - 80, HEIGHT / 2 + 50);
return;
}
player.draw(g2d);
for (Enemy e : enemies) e.draw(g2d);
for (Missile m : missiles) m.draw(g2d);
}
@Override
public void actionPerformed(ActionEvent e) {
if (!gameOver) {
update();
}
repaint();
}
private void update() {
player.update();
// Enemy firing logic (every 2 seconds)
if (System.currentTimeMillis() - lastEnemyFire > 2000) {
for (Enemy en : enemies) {
missiles.add(new Missile(en.x, en.y + 15, -5, Color.RED, false));
}
lastEnemyFire = System.currentTimeMillis();
}
// Move missiles and check collisions
Iterator<Missile> mIter = missiles.iterator();
while (mIter.hasNext()) {
Missile m = mIter.next();
m.update();
if (m.x < 0 || m.x > WIDTH) {
mIter.remove();
continue;
}
if (m.fromPlayer) {
Iterator<Enemy> eIter = enemies.iterator();
while (eIter.hasNext()) {
Enemy en = eIter.next();
if (m.getBounds().intersects(en.getBounds())) {
eIter.remove();
mIter.remove();
break;
}
}
} else {
if (m.getBounds().intersects(player.getBounds())) {
gameOver = true;
}
}
}
for (Enemy en : enemies) en.update(WIDTH, HEIGHT);
}
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_W) player.dy = -4;
if (key == KeyEvent.VK_S) player.dy = 4;
if (key == KeyEvent.VK_A) player.dx = -4;
if (key == KeyEvent.VK_D) player.dx = 4;
if (key == KeyEvent.VK_SPACE && !gameOver) {
missiles.add(new Missile(player.x + 40, player.y + 15, 7, Color.YELLOW, true));
}
if (key == KeyEvent.VK_R && gameOver) {
gameOver = false;
initGame();
}
}
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_W || key == KeyEvent.VK_S) player.dy = 0;
if (key == KeyEvent.VK_A || key == KeyEvent.VK_D) player.dx = 0;
}
@Override public void keyTyped(KeyEvent e) {}
}
abstract class Submarine {
double x, y;
double rotation = 0;
Color color;
public Submarine(double x, double y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void draw(Graphics2D g2d) {
AffineTransform old = g2d.getTransform();
g2d.translate(x, y);
// Submarine Body
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(0, 0, 50, 25)); // Hull
g2d.fill(new Rectangle2D.Double(15, -10, 15, 15)); // Conning tower
// Propeller (4 spokes spinning)
g2d.setColor(Color.BLACK);
g2d.translate(0, 12.5); // Move to back center
rotation += 0.2;
g2d.rotate(rotation);
for (int i = 0; i < 4; i++) {
g2d.rotate(Math.PI / 2);
g2d.draw(new Line2D.Double(0, 0, 0, 10));
}
g2d.setTransform(old);
}
public Rectangle2D getBounds() {
return new Rectangle2D.Double(x, y - 10, 50, 35);
}
}
class Player extends Submarine {
double dx, dy;
public Player(double x, double y) { super(x, y, Color.GRAY); }
public void update() {
x += dx;
y += dy;
}
}
class Enemy extends Submarine {
double vx, vy;
private Random r = new Random();
public Enemy(double x, double y) {
super(x, y, Color.RED);
vx = (r.nextDouble() - 0.5) * 2;
vy = (r.nextDouble() - 0.5) * 2;
}
public void update(int w, int h) {
x += vx;
y += vy;
// Bounce off walls
if (x < 300 || x > w - 60) vx *= -1;
if (y < 50 || y > h - 50) vy *= -1;
}
}
class Missile {
double x, y, speed;
Color color;
boolean fromPlayer;
public Missile(double x, double y, double speed, Color color, boolean fromPlayer) {
this.x = x;
this.y = y;
this.speed = speed;
this.color = color;
this.fromPlayer = fromPlayer;
}
public void update() { x += speed; }
public void draw(Graphics2D g2d) {
g2d.setColor(color);
g2d.fill(new Rectangle2D.Double(x, y, 10, 4));
}
public Rectangle2D getBounds() { return new Rectangle2D.Double(x, y, 10, 4); }
}
Key Logic Features:
Graphics2D & Geom: The submarine uses
Ellipse2Dfor the hull andRectangle2Dfor the tower. The propeller is animated usingAffineTransformto rotate 4Line2D"spokes" around the rear of the sub.Game Loop: Uses a
javax.swing.Timerset to 16ms to achieve roughly 60 frames per second.Movement:
KeyListenercaptures WASD inputs to modify the player's delta values (, ).Enemies: 20 red submarines are instantiated with random velocities. They "bounce" within a defined boundary on the right side of the screen.
Collision Detection: Every frame, the game iterates through missiles and checks if their bounding box (
Rectangle2D) intersects with a submarine's bounding box.Game Over: If a missile from an enemy hits the player's bounds, the
gameOverflag stops the update logic and displays the end screen.
How are you planning to expand this—maybe adding a scoring system or different levels?

Tidak ada komentar:
Posting Komentar