gpt4 book ai didi

java - 瓦片 map 中的命中检测

转载 作者:太空宇宙 更新时间:2023-11-04 06:46:47 27 4
gpt4 key购买 nike

我正在开发一款 Mario 游戏,需要有关如何为图 block map 创建命中检测的帮助和建议。

目前,玩家可以步行/跳跃穿过方 block 。我现在在地面上添加了一个固定检测,我希望用常规的命中检测来替换它。

我知道每个方 block 和玩家都有四个面。只有某些 block 需要命中检测,您可能需要知道的一些事情是玩家在 98% 的时间里保持在 300px(屏幕中间)。

唯一移动的是 map

map 是从 .txt 文件渲染的,渲染方式如下:

for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;

if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}

g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),
(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),
index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,
(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);
}
}
//This code is actually longer(included file later on)

对于多色图 block ,颜色命中检测太慢且不一致

由于 map 正在移动,我想我需要随之移动命中检测框。至于选择它应该检测的框可能很困难。也许让代码不命中检测某些图 block 会是一个更好的主意。

我的尝试以代码混淆而告终。谁能建议最简单的方法来实现命中检测? (记住我有跳跃)。

下面列出了重要的代码:

Board.java(绘制所有内容的面板)

package EvilMario;                                                                                          //Include this class in the EvilMario game package

import java.awt.*; //Imported to allow use of Image
import java.awt.event.*; //Imported to allow use of ActionListener

import javax.swing.*; //Import swing

public class Board extends JPanel implements ActionListener { //Class Board
private TileLayer l; //Instance of TileLayer class
private Menu m; //Instance of menu class
private Player p; //Instance of player class

Timer time; //A timer

public static enum STATE {MENU,GAME}; //The game states
public static STATE State = STATE.MENU; //Set the first state to menu

//END
//GLOBAL
//DECLARATIONS

public Board() {
l = TileLayer.FromFile("D:/ICS3U1/EvilMario/map.txt"); //Tile map data from .txt file
this.addMouseListener(new MouseInput()); //Listen for mouse input
this.addKeyListener(new AL()); //Listen for key input

p = new Player(); //Start running Player class
m = new Menu(); //Start running Menu class

setFocusable(true); //Allows movement

time = new Timer(20,this); //Timer set to update "this" class every 20 milliseconds(Approximately 50fps)
time.start(); //Actually start the timer
}

public void actionPerformed(ActionEvent e) {
p.move(); //Call the move method from the player class
repaint(); //Repaint
}

public void paintComponent(Graphics g) { //Graphics method
super.paintComponent(g); //Super hero?
Graphics2D g2d = (Graphics2D) g; //Cast 2D graphics

if(State==STATE.GAME) {
if(p.distanceTraveled<300)l.DrawLayer(g,0);else l.DrawLayer(g, -(p.distanceTraveled-300)); //Draw the tile map

g2d.drawImage(p.getImage(), p.getX(), p.getY(), 48, 48, null); //Draw the player

if(p.distanceTraveled==3488) System.out.println("You have won the game!"); //Draw the end game screen
} else {
m.render(g); //Render the menu
}
}

private class AL extends KeyAdapter { //Action Listener extends key adapter
public void keyPressed(KeyEvent e) { //On key press
p.keyPressed(e); //Send whatever key was pressed TO the keyPressed method in the player class
}
public void keyReleased(KeyEvent e) { //On key release
p.keyReleased(e); //Send whatever key was released TO the keyReleased method in the player class
}
}
}

Player.java(玩家逻辑)

package EvilMario;                                                                                          //Include this class in the EvilMario game package
import java.awt.Image;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;

public class Player {
int x, dx, y, distanceTraveled; //x coordinate,change in x coordinate,y coordinate,1st rep bg,2nd rep bg,dist traveled
Image player; //The player variable
ImageIcon walk_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_anim.gif");
ImageIcon walk_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_idle.png");
ImageIcon walk_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_anim.gif");
ImageIcon walk_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_idle.png");

ImageIcon jump_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_anim.gif");
ImageIcon jump_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_idle.png");
ImageIcon jump_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_anim.gif");
ImageIcon jump_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_idle.png");

boolean holdingLeft = false;
boolean holdingRight = false;
static boolean jumping = false;
static boolean falling = false;
static int jumpingTime = 350;

public Player() {
player = walk_R_idle.getImage(); //Give the player the image
x = 75; //The original x position of the player
y = 277; //The original y position of the player
distanceTraveled = 75; //Original distance traveled
}

public void move() {
if(x>=0 && x<=300) { //If the player is within the moving area
x = x+dx; //The x position is updated to become itself+the amount you moved
}
if(x<0) //If the player has reached he very left side of the screen(0px)
x=0; //Move him up a pixel so he can move again
if(x>300) //If the player has reached the center of the screen(300px)
x=300; //Move him down a pixel so he can move again

distanceTraveled=distanceTraveled+dx; //Calculate distanceTraveled

if(distanceTraveled<0) //Make sure distanceTraveled isn't a negative
distanceTraveled=0; //Make sure distanceTraveled isn't a negative
if(distanceTraveled>=300) //Keep player at center position once past 300 mario meters
x=300; //Keep player at center position once past 300 mario meters

if(holdingLeft && !holdingRight) {
if(distanceTraveled<300)dx=-5; else dx=-4;

if(jumping && !falling) {
player = jump_L_anim.getImage();
y-=8;
} else {
player = walk_L_anim.getImage();
if(y<277)
y+=8;
}
} else if(holdingRight && !holdingLeft) {
if(distanceTraveled<300)dx=5; else dx=4;

if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
player = walk_R_anim.getImage();
if(y<277)
y+=8;
}
} else if(!holdingRight && !holdingLeft) {
dx = 0;

if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
if(y<277)
y+=8;
}
}

if(y==277) {
falling = false;
}

System.out.println("LEFT: "+holdingLeft+" JUMP: "+jumping+" RIGHT: "+holdingRight+" FALLING: "+falling+" Y: "+y);
}

public int getX() { return x; } //This method will return the x. Is used by other classes
public int getY() { return y; } //This method will return the y. Is used by other classes
public Image getImage() { return player; } //This method will return the player. Is used by other classes

public void keyPressed(KeyEvent e) { //Called from the board class, the argument is whatever key was pressed
int key = e.getKeyCode(); //The key originally sent from the board class

if(key == KeyEvent.VK_LEFT && !holdingLeft)
holdingLeft = true;
if(key == KeyEvent.VK_RIGHT && !holdingRight)
holdingRight = true;
if(key == KeyEvent.VK_UP && !jumping && !falling)
new Thread(new JumpThread(this)).start();
}

public void keyReleased(KeyEvent e) { //Called from the board class, the argument is whatever key was released
int key = e.getKeyCode(); //The key originally sent from the board class

if(key == KeyEvent.VK_LEFT) { //If the left or right key was released
dx = 0; //Stop moving
holdingLeft = false;
player = walk_L_idle.getImage();
}

if(key == KeyEvent.VK_RIGHT) {
dx = 0;
holdingRight = false;
player = walk_R_idle.getImage();
}
}
}

TileLayer.java(图 block 层的渲染)(可能是与问题相关的最重要的部分)

package EvilMario;                                                                           //Include this class in the EvilMario game package

import java.awt.Graphics; //

public class TileLayer {

private int[][] map; //2D array
private BufferedImage tileSheet; //The tile sheet

public TileLayer(int[][] existingMap) { //
map = new int[existingMap.length][existingMap[0].length]; //map initialized
for(int y=0;y<map.length;y++) { //Loop through all boxes
for(int x=0;x<map[y].length;y++) { //Loop through all boxes
map[y][x] = existingMap[y][x]; //Update the map
}
}
tileSheet = LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif"); //Load the tilesheet
}

public TileLayer(int width, int height) {
map = new int[height][width];
}

public static TileLayer FromFile(String fileName) {
TileLayer layer = null;

ArrayList<ArrayList<Integer>> tempLayout = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String currentLine;

while((currentLine = br.readLine()) !=null) {
if(currentLine.isEmpty())
continue;

ArrayList<Integer> row = new ArrayList<>();
String[] values = currentLine.trim().split(" ");

for(String string: values) {
if(!string.isEmpty()) {
int id = Integer.parseInt(string);
row.add(id);
}
}
tempLayout.add(row);
}
} catch(IOException e) {
System.out.println("ERROR");
}

int width = tempLayout.get(0).size();
int height = tempLayout.size();

layer = new TileLayer(width,height);
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
layer.map[y][x] = tempLayout.get(y).get(x);
}
}
layer.tileSheet = layer.LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif");

return layer;
}

public BufferedImage LoadTileSheet(String fileName) {
BufferedImage img = null;

try {
img = ImageIO.read(new File(fileName));
} catch(Exception e) {
System.out.println("Could not load image");
}
return img;
}

int scale = 2;

public void DrawLayer(Graphics g, int position) {

for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;

if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}

g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),

(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),

index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,

(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);

}
}
}

}

Engine.java(不太重要)(图 block 大小的简单变量)

package EvilMario;

public class Engine {
public static final int TILE_WIDTH = 16;
public static final int TILE_HEIGHT = 16;
}

如果您需要其他代码片段,只需询问即可。我并不是要求您给我这个问题的具体答案,而只是要求您提供一种适用于我的以下代码的方法。

  • 如果有具体的答案就更好了:)

我也相信这个问题的答案对其他人很有用,因为这个方法在流行的 java 2d 游戏教程视频中进行了解释(他们从未展示过命中检测)。

我尝试过的方法:

使用 TileLayer.java 中存储数组位置的确切代码创建一个名为 HitDetectionLayer 的新 java 文件。失败了:(

最佳答案

好吧,我不完全确定你在做什么,如果你抛出一些图像会更清楚。

无论如何,“命中检测”又名碰撞检测是一个非常复杂的主题,但这取决于您想要做什么。如果你希望所有东西都是方框或圆圈,那么这很容易。然而,如果你想让物体旋转或者你想要复杂形状的碰撞,那就变得极其困难。

大多数游戏使用圆形或球体进行碰撞。您将大部分图形放入其中(它可能不完全适合将部分图像留在圆圈内或圆圈外,但这就是生活)。现在假设您有马里奥 Sprite 和其中一只海龟。嗯,它们周围都有圆圈,一旦圆圈接触,您就会触发事件。

这个问题的数学计算非常简单,因为根据定义,圆是围绕恒定长度的周长。看看这个:

example1你可能已经知道这一点,而且看起来似乎很明显,但如果你仔细想想,这就是圆的真正含义:在每个可测方向上的长度都是一致的。方向以度为单位测量,从那里你可以继续学习三角学,但你不需要它。你需要的是协调,又名 vector 。所以看看这个:

enter image description here
确定圆碰撞所需的只是圆之间的距离。无论圆以什么角度碰撞都没有关系,因为到圆心的距离始终是一致的。即使圆的大小不同,也没关系,只需考虑半径差异即可。

如果要计算所有这些,您可以编写如下方法:

public boolean testDistanceBetween( float radius1, float radius2, 
float x1, float x2, float y1, float y2 ){

double distanceBetween = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

if(distanceBetween < (radius1+radius2) ){
return true;
}

return false;
}

这个故事的寓意是,圆圈就是这样的。如果你想进行矩形碰撞,你可以取左下角和右上角的点,然后测试这些点之间是否有其他矩形。这应该很简单,每个点都是一个 vector ,每个矩形有 4 个点。如果一个矩形的 4 个点中的任何一个位于另一个矩形上的点之间,则存在碰撞。

您也可以使用该系统来处理地面和墙壁。例如,如果地面的 Y=300,那么如果您的 Sprite 的 y 坐标 == 300,您将暂停重力。

我想解释的主要事情是,如果您打算有旋转的矩形或多边形,并且想要检测它们的碰撞......祝您好运。是的,可以做到,但你应该明白你正在实现复杂的物理,特别是当/如果你实现重力时。

所以我的回答是谨慎的:没有简单的方法来检测旋转矩形或多边形的碰撞。圆形和静态矩形是限制。如果您确实想要旋转矩形/多边形,请使用物理引擎。 Box2d 相当不错,有一个 Java 版本的 Jbox2d。

关于java - 瓦片 map 中的命中检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23860009/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com