[英]Moving two objects in the same class on a JFrame in Java
我有一个正在处理的项目,因为我必须创建两个对象,每次我在小矩形(红色)移动时按下键盘上的箭头键时,我希望大矩形(蓝色)在框架周围移动离开,一旦大方块接触/标记小矩形,屏幕就会刷新,我可以再次移动大矩形来追逐小矩形。 下面是我的主要 class 和 IT class,我在其中实现了两种形状。
目标是让两个矩形,每次大矩形靠近时小矩形都会在框架中跑开,直到它被标记。 还必须在框架上添加某种分数面板以显示更新的分数和一个计时器以在玩家开始播放时倒计时。
我需要帮助让两个矩形以不同方式移动而不是彼此重叠。 每当第一个矩形靠近它时,我希望第二个矩形在框架周围移动时移开
`
My class IT
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class IT extends JPanel implements ActionListener, KeyListener {
Timer shapeTimer = new Timer(5, this);
public double xPos = 0, yPos = 0, movementX = 0, movementY = 0;
public int rectSize = 50;
public int rectSize2 = 35;
public int windowWidth;
int windowHeight;
public int xBound;
public int yBound;
public IT(int w, int h){
shapeTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
windowWidth = w;
windowHeight = h;
xBound = (windowWidth - rectSize);
yBound = (windowHeight - rectSize);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D movableRect = new Rectangle2D.Double(xPos, yPos, rectSize, rectSize);
g2.setColor(Color.BLUE);
g2.draw(movableRect);
g2.fill(movableRect);
Rectangle2D movableRect2 = new Rectangle2D.Double(xPos, yPos, rectSize2, rectSize2);
g2.setColor(Color.RED);
g2.draw(movableRect2);
g2.fill(movableRect2);
}
public void actionPerformed(ActionEvent e){
repaint();
xPos += movementX;
yPos += movementY;
}
public void moveUp(){
if (yPos == 0){
movementY = 0;
movementX = 0;
}
movementY = -0.5;
movementX = 0;
}
public void moveDown(){
if (yPos == yBound){
movementY = 0;
movementX = 0;
}
movementY = 0.5;
movementX = 0;
}
public void moveLeft()
{
if (xPos == 0){
movementY = 0;
movementX = 0;
}
movementX = -0.5;
movementY = 0;
}
public void moveRight(){
if (xPos == xBound)
{
movementY = 0;
movementX = 0;
}
movementX = 0.5;
movementY = 0;
}
public void enlargeSquare(){
rectSize++;
rectSize2++;
}
public void shrinkSquare(){
rectSize--;
rectSize2--;
}
public void keyPressed(KeyEvent e){
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP){
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN){
moveDown();
}
if (keyCode == KeyEvent.VK_RIGHT){
moveRight();
}
if (keyCode == KeyEvent.VK_LEFT){
moveLeft();
}
if (keyCode == KeyEvent.VK_OPEN_BRACKET)
{
shrinkSquare();
}
if (keyCode == KeyEvent.VK_CLOSE_BRACKET)
{
enlargeSquare();
}
}
public void keyTyped(KeyEvent e){
}
public void keyReleased(KeyEvent e){
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_DOWN){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_RIGHT){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_UP){
movementX = 0;
movementY = 0;
}
}
}
` MainTester class
`
import javax.swing.*;
import javax.swing.JFrame;
public class MainTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
int frameWidth = 850;
int frameHeight = 650;
JFrame frmMain = new JFrame();
frmMain.setSize(frameWidth, frameHeight);
IT it = new IT(frameWidth, frameHeight);
frmMain.add(it);
frmMain.setVisible(true);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmMain.setTitle("Tag Game");
}
}
`
使用键绑定,说真的,这将解决 swagger 与KeyListener
相关的问题。
解耦和分离你的逻辑。 每个“实体”应该是一个独立的工作单元。 在您的情况下,它们至少应包含有关颜色、位置和大小的信息。
为简单起见,我从一些可以涂漆的东西开始......
public interface Entity {
public void paint(Graphics2D g2d);
}
现在,你可以有很多不同的interface
来反映它,它可以被绘制、移动、控制、代表效果或任何你需要的东西——然后你的类应该实现他们需要的interface
。 然后,您的引擎只处理它想要的“概念”——在需要时需要所有“可绘制”实体,需要所有“可移动”实体等。
接下来我创建了一个“玩家”的概念。 玩家是一个“可绘制”实体,但可以通过某种方式由玩家控制,因此它采用当前 state 的“输入”并根据这些状态更新自身(更多信息即将到来)
public static class PlayerEntity implements Entity {
protected static final int DELTA = 2;
private Rectangle bounds = new Rectangle(0, 0, 35, 35);
private Color fillColor;
public PlayerEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void update(Set<PlayerAction> actions, Dimension size) {
Rectangle currentBounds = getBounds();
int x = currentBounds.x;
int y = currentBounds.y;
if (actions.contains(PlayerAction.UP)) {
y -= DELTA;
}
if (actions.contains(PlayerAction.DOWN)) {
y += DELTA;
}
if (actions.contains(PlayerAction.LEFT)) {
x -= DELTA;
}
if (actions.contains(PlayerAction.RIGHT)) {
x += DELTA;
}
if (y < 0) {
y = 0;
}
if (y + currentBounds.height > size.height) {
y = size.height - currentBounds.height;
}
if (x < 0) {
x = 0;
}
if (x + currentBounds.width > size.width) {
x = size.width - currentBounds.width;
}
getBounds().setLocation(x, y);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
作为参考, PlayerAction
表示玩家可以执行的所有有效动作,为了简单起见,我只坚持移动:
public enum PlayerAction {
UP, DOWN, LEFT, RIGHT;
}
接下来我创建了一个“怪物”实体,在这种情况下,“怪物”将始终尝试并跟随玩家,此实现大致基于Java:将图像移向鼠标 position
public static class MonsterEntity implements Entity {
protected static final int DELTA = 1;
private Rectangle bounds = new Rectangle(0, 0, 15, 15);
private Color fillColor;
public MonsterEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void moveTowards(Point target) {
Rectangle bounds = getBounds();
Point center = getCenter();
int xDelta = target.x < center.x ? -DELTA : DELTA;
int yDelta = target.y < center.y ? -DELTA : DELTA;
getBounds().setLocation(bounds.x + xDelta, bounds.y + yDelta);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
怪物总是试图将它的中心移动到目标位置(最终将成为玩家的中心)
现在,这就是事情变得复杂的地方。
我们需要:
为简单起见,我从JPanel
开始,使用 Swing Timer
和键绑定 API。
public class MainPane extends JPanel {
// This represents the "input bindings", these represent
// abstract actions which can be applied to the player
// or game state.
private enum InputKey {
PRESSED_UP, PRESSED_DOWN, PRESSED_LEFT, PRESSED_RIGHT,
RELEASED_UP, RELEASED_DOWN, RELEASED_LEFT, RELEASED_RIGHT;
public KeyStroke getKeyStroke() {
switch (this) {
case PRESSED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
case PRESSED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false);
case PRESSED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false);
case PRESSED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
case RELEASED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);
case RELEASED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true);
case RELEASED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true);
case RELEASED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);
}
return null;
}
}
private PlayerEntity playerEntity;
private MonsterEntity monsterEntity;
private Timer timer;
private Set<PlayerAction> actions = new HashSet<PlayerAction>();
public MainPane() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(InputKey.PRESSED_UP.getKeyStroke(), InputKey.PRESSED_UP);
inputMap.put(InputKey.PRESSED_DOWN.getKeyStroke(), InputKey.PRESSED_DOWN);
inputMap.put(InputKey.PRESSED_LEFT.getKeyStroke(), InputKey.PRESSED_LEFT);
inputMap.put(InputKey.PRESSED_RIGHT.getKeyStroke(), InputKey.PRESSED_RIGHT);
inputMap.put(InputKey.RELEASED_UP.getKeyStroke(), InputKey.RELEASED_UP);
inputMap.put(InputKey.RELEASED_DOWN.getKeyStroke(), InputKey.RELEASED_DOWN);
inputMap.put(InputKey.RELEASED_LEFT.getKeyStroke(), InputKey.RELEASED_LEFT);
inputMap.put(InputKey.RELEASED_RIGHT.getKeyStroke(), InputKey.RELEASED_RIGHT);
actionMap.put(InputKey.PRESSED_UP, new MoveAction(actions, PlayerAction.UP, true));
actionMap.put(InputKey.PRESSED_DOWN, new MoveAction(actions, PlayerAction.DOWN, true));
actionMap.put(InputKey.PRESSED_LEFT, new MoveAction(actions, PlayerAction.LEFT, true));
actionMap.put(InputKey.PRESSED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, true));
actionMap.put(InputKey.RELEASED_UP, new MoveAction(actions, PlayerAction.UP, false));
actionMap.put(InputKey.RELEASED_DOWN, new MoveAction(actions, PlayerAction.DOWN, false));
actionMap.put(InputKey.RELEASED_LEFT, new MoveAction(actions, PlayerAction.LEFT, false));
actionMap.put(InputKey.RELEASED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, false));
Dimension size = getPreferredSize();
Point center = new Point((size.width - 35) / 2, (size.height - 35) / 2);
playerEntity = new PlayerEntity(Color.BLUE, center);
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
}
@Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
performTick();
}
});
timer.start();
}
@Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void performTick() {
playerEntity.update(actions, getSize());
monsterEntity.moveTowards(playerEntity.getCenter());
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(g2d);
monsterEntity.paint(g2d);
g2d.dispose();
}
}
移动(或输入)是通过键绑定 API 控制的,这会触发一个MoveAction
,它会更新集中式 state 存储库(然后传递给PlayerEntity
,以便它相应地应用 state)。
为简单起见,我只使用了一个Action
,但你可以做一对,一个代表“按下/激活”或“释放/停用”
public class MoveAction extends AbstractAction {
private Set<PlayerAction> actions;
private PlayerAction action;
private boolean activate;
public MoveAction(Set<PlayerAction> directions, PlayerAction direction, boolean activate) {
this.actions = directions;
this.action = direction;
this.activate = activate;
}
@Override
public void actionPerformed(ActionEvent e) {
if (activate) {
actions.add(action);
} else {
actions.remove(action);
}
}
}
有关操作的更多详细信息,请参阅如何使用操作。
Timer
和“paint”工作流分离出来以分离类,进一步解耦类。KeyListener
怪异相关的所有问题。 它还分离了输入 - 想要添加触摸控件/按钮,不用担心,它是通过Action
s 完成的。 想要添加操纵杆/控制器,不用担心,这是通过Action
s 完成的。想要更多的怪物?
改变:
private MonsterEntity monsterEntity;
到:
private List<MonsterEntity> monsterEntitys = new ArrayList<>(32);
改变:
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
到:
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(0, 0)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(size.width - 15, 0)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(0, size.height - 15)));
改变:
monsterEntity.paint(g2d);
到:
for (MonsterEntity entity : monsterEntitys) {
entity.paint(g2d);
}
现在你有更多的怪物! 玩得开心!
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum PlayerAction {
UP, DOWN, LEFT, RIGHT;
}
public class MainPane extends JPanel {
private enum InputKey {
PRESSED_UP, PRESSED_DOWN, PRESSED_LEFT, PRESSED_RIGHT,
RELEASED_UP, RELEASED_DOWN, RELEASED_LEFT, RELEASED_RIGHT;
public KeyStroke getKeyStroke() {
switch (this) {
case PRESSED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
case PRESSED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false);
case PRESSED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false);
case PRESSED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
case RELEASED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);
case RELEASED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true);
case RELEASED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true);
case RELEASED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);
}
return null;
}
}
private PlayerEntity playerEntity;
private MonsterEntity monsterEntity;
private Timer timer;
private Set<PlayerAction> actions = new HashSet<PlayerAction>();
public MainPane() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(InputKey.PRESSED_UP.getKeyStroke(), InputKey.PRESSED_UP);
inputMap.put(InputKey.PRESSED_DOWN.getKeyStroke(), InputKey.PRESSED_DOWN);
inputMap.put(InputKey.PRESSED_LEFT.getKeyStroke(), InputKey.PRESSED_LEFT);
inputMap.put(InputKey.PRESSED_RIGHT.getKeyStroke(), InputKey.PRESSED_RIGHT);
inputMap.put(InputKey.RELEASED_UP.getKeyStroke(), InputKey.RELEASED_UP);
inputMap.put(InputKey.RELEASED_DOWN.getKeyStroke(), InputKey.RELEASED_DOWN);
inputMap.put(InputKey.RELEASED_LEFT.getKeyStroke(), InputKey.RELEASED_LEFT);
inputMap.put(InputKey.RELEASED_RIGHT.getKeyStroke(), InputKey.RELEASED_RIGHT);
actionMap.put(InputKey.PRESSED_UP, new MoveAction(actions, PlayerAction.UP, true));
actionMap.put(InputKey.PRESSED_DOWN, new MoveAction(actions, PlayerAction.DOWN, true));
actionMap.put(InputKey.PRESSED_LEFT, new MoveAction(actions, PlayerAction.LEFT, true));
actionMap.put(InputKey.PRESSED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, true));
actionMap.put(InputKey.RELEASED_UP, new MoveAction(actions, PlayerAction.UP, false));
actionMap.put(InputKey.RELEASED_DOWN, new MoveAction(actions, PlayerAction.DOWN, false));
actionMap.put(InputKey.RELEASED_LEFT, new MoveAction(actions, PlayerAction.LEFT, false));
actionMap.put(InputKey.RELEASED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, false));
Dimension size = getPreferredSize();
Point center = new Point((size.width - 35) / 2, (size.height - 35) / 2);
playerEntity = new PlayerEntity(Color.BLUE, center);
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
}
@Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
performTick();
}
});
timer.start();
}
@Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void performTick() {
playerEntity.update(actions, getSize());
monsterEntity.moveTowards(playerEntity.getCenter());
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(g2d);
monsterEntity.paint(g2d);
g2d.dispose();
}
}
public class MoveAction extends AbstractAction {
private Set<PlayerAction> actions;
private PlayerAction action;
private boolean activate;
public MoveAction(Set<PlayerAction> directions, PlayerAction direction, boolean activate) {
this.actions = directions;
this.action = direction;
this.activate = activate;
}
@Override
public void actionPerformed(ActionEvent e) {
if (activate) {
actions.add(action);
} else {
actions.remove(action);
}
}
}
public interface Entity {
public void paint(Graphics2D g2d);
}
public static class MonsterEntity implements Entity {
protected static final int DELTA = 1;
private Rectangle bounds = new Rectangle(0, 0, 15, 15);
private Color fillColor;
public MonsterEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void moveTowards(Point target) {
Rectangle bounds = getBounds();
Point center = getCenter();
int xDelta = target.x < center.x ? -DELTA : DELTA;
int yDelta = target.y < center.y ? -DELTA : DELTA;
getBounds().setLocation(bounds.x + xDelta, bounds.y + yDelta);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
public static class PlayerEntity implements Entity {
protected static final int DELTA = 2;
private Rectangle bounds = new Rectangle(0, 0, 35, 35);
private Color fillColor;
public PlayerEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void update(Set<PlayerAction> actions, Dimension size) {
Rectangle currentBounds = getBounds();
int x = currentBounds.x;
int y = currentBounds.y;
if (actions.contains(PlayerAction.UP)) {
y -= DELTA;
}
if (actions.contains(PlayerAction.DOWN)) {
y += DELTA;
}
if (actions.contains(PlayerAction.LEFT)) {
x -= DELTA;
}
if (actions.contains(PlayerAction.RIGHT)) {
x += DELTA;
}
if (y < 0) {
y = 0;
}
if (y + currentBounds.height > size.height) {
y = size.height - currentBounds.height;
}
if (x < 0) {
x = 0;
}
if (x + currentBounds.width > size.width) {
x = size.width - currentBounds.width;
}
getBounds().setLocation(x, y);
}
@Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
}
现在速度/增量实际上非常高。 我会考虑使用Shape
API,使用Point2D
和Rectangle2D
之类的东西,它们提供基于double
的属性而不是int
,这会减少增量值并减慢实体的速度。
有关更多详细信息,请参阅 使用几何
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.