【发布时间】:2017-09-07 20:28:58
【问题描述】:
我正在尝试在 Java 中绘制等距瓷砖并使用鼠标光标实现瓷砖拾取系统。我使用我找到并适应我的瓷砖纹理的这些数学公式绘制瓷砖,您可以在下面找到。瓷砖是 64x64 像素,但即使我使用 64x64 精灵绘制平面瓷砖,它们的高度也只有 32 像素。
地图是一个简单的二维数组,其中我的图块由它们的 id 表示。
这是我使用 toIso() 函数将地图坐标转换为屏幕坐标的类。我将代表屏幕上光标位置的屏幕坐标传递给 toGrid() 函数以将它们转换为地图坐标。
public class Utils {
private static int TILE_WIDTH = Tile.TILE_WIDTH;
private static int TILE_HEIGHT = Tile.TILE_HEIGHT;
private static int TILE_WIDTH_HALF = TILE_WIDTH/2;
private static int TILE_HEIGHT_HALF = TILE_HEIGHT/2;
private static int TILE_WIDTH_QUARTER = TILE_WIDTH_HALF/2;
private static int TILE_HEIGHT_QUARTER = TILE_HEIGHT_HALF/2;
public static int[] toIso(int x, int y){
int i = (x - y) * TILE_WIDTH_HALF;
int j = (x + y) * TILE_HEIGHT_QUARTER;
//800 and 100 are temporary offsets I apply to center the map.
i+=800;
j+=100;
return new int[]{i,j};
}
public static int[] toGrid(int x, int y){
//800 and 100 are temporary offsets I apply to center the map.
x-=800;
y-=100;
int i = ( x / ( TILE_WIDTH_HALF ) + y / ( TILE_HEIGHT_QUARTER )) / 2;
int j = ( y / ( TILE_HEIGHT_QUARTER ) - ( x / ( TILE_WIDTH_HALF ))) / 2;
return new int[]{i,j};
}}
我目前使用两个 for 循环来渲染我的图块,并使用 toIso() 函数将地图坐标转换为屏幕坐标。
public void render(Graphics g){
for(int x = 0;x<width;x++){
for(int y = 0;y<height;y++){
int[] isoCoords = Utils.toIso(x, y);
int fx = isoCoords[0];//
int fy = isoCoords[1];//
if(world[x][y] == 0){
Tile grass = new GrassTile(0);
grass.render(g, grass.getId(), fx, fy);
}else if(world[x][y] == 1){
Tile water = new WaterTile(1);
water.render(g, water.getId(), fx, fy);
}
}
}
}
我得到了一个我想要在屏幕上渲染的菱形。
我终于更新了屏幕上实际鼠标坐标的每个刻度。
int[] coords = Utils.toGrid(mouseManager.getMouseX(), mouseManager.getMouseY());
tileX = coords[0];
tileY = coords[1];
最终渲染选定的图块:
BufferedImage selectedTexture = Assets.selected;
int[] coordsIsoSelected = Utils.toIso(this.tileX, this.tileY);
g.drawImage(selectedTexture, coordsIsoSelected[0], coordsIsoSelected[1], Tile.TILE_WIDTH, Tile.TILE_HEIGHT, null);
g.drawRect(Utils.toIso(tileX, tileY)[0], Utils.toIso(tileX, tileY)[1]+Tile.TILE_HEIGHT/2, Tile.TILE_WIDTH, Tile.TILE_HEIGHT/2);//I draw a rectangle to visualize what's happening.
最后,我的图块检测没有按预期工作,它不能完美地拟合图块,但它似乎与我绘制的矩形有关。我无法弄清楚这个问题的解决方案,我提前感谢您阅读或您可以给我的任何建议。如果您需要更高的精度,我很乐意为您提供更多信息。
这是一个展示实际情况的视频:youtu.be/baCVIfJz2Wo
编辑:
这是我的一些代码,您可以使用它来运行像我这样的应用程序。对于这个非常混乱的代码,我很抱歉,但我试图让它尽可能短,而不影响“游戏”的行为。
您需要将之前提供的工作表放入项目的资源文件夹中创建的“纹理”文件夹中。
gfx 包:
package fr.romainimberti.isometric.gfx;
import java.awt.image.BufferedImage;
public class Assets {
private static final int width = 64, height = 64;
public static BufferedImage grass, water, selected;
public static void init(){
//Temp
SpriteSheet tileSheet = new SpriteSheet(ImageLoader.loadImage("/textures/sheet.png"));
grass = tileSheet.crop(width*2, 0, width, height);
water = tileSheet.crop(width*9, height*5, width, height);
selected = tileSheet.crop(0, height*5, width, height);
//
}
}
package fr.romainimberti.isometric.gfx;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageLoader {
public static BufferedImage loadImage(String path){
try {
return ImageIO.read(ImageLoader.class.getResource(path));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
return null;
}
}
package fr.romainimberti.isometric.gfx;
import java.awt.image.BufferedImage;
public class SpriteSheet {
private BufferedImage sheet;
public SpriteSheet(BufferedImage sheet){
this.sheet = sheet;
}
public BufferedImage crop(int x, int y, int width, int height){
return sheet.getSubimage(x, y, width, height);
}
}
项目的其余部分:
package fr.romainimberti.isometric;
public class Launcher {
public static void main(String args[]){
System.setProperty("sun.awt.noerasebackground", "true");
Game game = new Game("Isometric", 1280, 720);
game.start();
}
}
package fr.romainimberti.isometric;
import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
public class Display {
private JFrame frame;
private Canvas canvas;
private String title;
private int width, height;
public Display(String title, int width, int height){
this.title = title;
this.width = width;
this.height = height;
createDisplay();
}
private void createDisplay(){
frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
canvas.setFocusable(true);
frame.add(canvas);
frame.pack();
}
public Canvas getCanvas(){
return canvas;
}
public JFrame getFrame(){
return frame;
}
}
package fr.romainimberti.isometric;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import fr.romainimberti.isometric.gfx.Assets;
public class Game implements Runnable {
private Display display;
private int width, height;
public JFrame frame;
private boolean running = false;
private Thread thread;
public String title;
private BufferStrategy bs;
private Graphics g;
public int x, y;
public int[][] world;
public static final int TILE_WIDTH = 64;
public static final int TILE_HEIGHT = 64;
public static final int TILE_WIDTH_HALF = 32;
public static final int TILE_HEIGHT_HALF = 32;
public static final int TILE_WIDTH_QUARTER = 16;
public static final int TILE_HEIGHT_QUARTER = 16;
public int xOffset;
//Input
private MouseManager mouseManager;
public Game(String title, int width, int height){
this.width = width;
this.height = height;
this.mouseManager = new MouseManager(this);
this.world = new int[10][10];
}
private void init(){
display = new Display(title, width, height);
display.getFrame().addMouseListener(mouseManager);
display.getFrame().addMouseMotionListener(mouseManager);
display.getCanvas().addMouseListener(mouseManager);
display.getCanvas().addMouseMotionListener(mouseManager);
this.frame = display.getFrame();
Assets.init();
xOffset = frame.getWidth()/2;
//Fill the world
for(int i = 0;i<world.length;i++){
for(int j=0;j<world[0].length;j++){
int r = ThreadLocalRandom.current().nextInt(0,1+1);
if(r == 0)
world[i][j] = 0;
else
world[i][j] = 1;
}
}
}
private void tick(){
mouseManager.tick();
xOffset = frame.getWidth()/2;
}
private void render(){
bs = display.getCanvas().getBufferStrategy();
if(bs == null){
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
//Clear Screen
g.clearRect(0, 0, frame.getWidth(), frame.getHeight());
//Draw Here
//World render
for(int x = 0;x<world.length;x++){
for(int y = 0;y<world[0].length;y++){
int[] isoCoords = toIso(x, y);
int fx = isoCoords[0];//
int fy = isoCoords[1];//
if(world[x][y] == 0){
g.drawImage(Assets.grass, fx, fy, null);
}else if(world[x][y] == 1){
g.drawImage(Assets.water, fx, fy, null);
}
}
}
//Selected tile render
int[] coordsIsoSelected = toIso(x, y);
g.drawImage(Assets.selected, coordsIsoSelected[0], coordsIsoSelected[1], TILE_WIDTH, TILE_HEIGHT, null);
//End Drawing
bs.show();
g.dispose();
}
public void run(){
init();
int fps = 120;
double timePerTick = 1000000000 / fps;
double delta = 0;
long now;
long lastTime = System.nanoTime();
while(running){
now = System.nanoTime();
delta += (now - lastTime) / timePerTick;
lastTime = now;
if(delta >= 1){
tick();
render();
delta--;
}
}
stop();
}
public MouseManager getMouseManager(){
return mouseManager;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static int[] toIso(int x, int y){
int i = (x - y) * TILE_WIDTH_HALF;
int j = (x + y) * TILE_HEIGHT_QUARTER;
i+=xOffset;
return new int[]{i,j};
}
public static int[] toGrid(int x, int y){
x-=xOffset;
int i = ( x / ( TILE_WIDTH_HALF ) + y / ( TILE_HEIGHT_QUARTER )) / 2;
int j = ( y / ( TILE_HEIGHT_QUARTER ) - ( x / ( TILE_WIDTH_HALF ))) / 2;
return new int[]{i,j};
}
}
package fr.romainimberti.isometric;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class MouseManager implements MouseListener, MouseMotionListener {
private boolean leftPressed, rightPressed;
private int mouseX, mouseY;
private Game game;
public MouseManager(Game game){
this.game = game;
}
public void tick(){
game.x = game.toGrid(mouseX, mouseY)[0];
game.y = game.toGrid(mouseX, mouseY)[1];
}
// Getters
public boolean isLeftPressed(){
return leftPressed;
}
public boolean isRightPressed(){
return rightPressed;
}
public int getMouseX(){
return mouseX;
}
public int getMouseY(){
return mouseY;
}
// Implemented methods
@Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1)
leftPressed = true;
else if(e.getButton() == MouseEvent.BUTTON3)
rightPressed = true;
}
@Override
public void mouseReleased(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1)
leftPressed = false;
else if(e.getButton() == MouseEvent.BUTTON3)
rightPressed = false;
}
@Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
}
@Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
如果需要,可以在这里找到我的项目架构,以便正确组织所有文件。
再次对这段非常非常混乱的代码感到抱歉,但我不得不拆分游戏中所有有用的部分以减小它的大小。也不要忘记下载并正确放置工作表文件。希望这会有所帮助。
【问题讨论】:
-
“如果您需要更高的精度,我很乐意为您提供更多信息。” 您可以提供的最高精度是minimal reproducible example,请发一个。另外,我不能完全理解你的问题,你到底想达到什么目的?顺便说一句,你在使用 Swing 吗?
-
你的鼠标在哪里?将它们悬停时会发生什么?现在发生了什么?如果你能提供一个 MCVE 会更容易理解
-
好的,视频让我清楚地知道了问题,但我们仍然需要minimal reproducible example,这样我们就可以复制粘贴并自己查看错误并尝试解决它:) 你能试试吗?做一个?这不是您的全部工作代码,它是一个新的简短示例(不是上面的代码 sn-ps),我们可以复制粘贴,不需要导入任何内容(即在您的 MCVE 中包含导入),这样我们就可以提供更多信息/答案和更高质量
-
什么是代码 sn-p?它们是显示程序特定部分的代码部分,通常很短。什么是整体程序?它包括程序需要运行的所有类,但它真的很大。什么是 MCVE?它是两者之间的中间点,它是一个完整的程序,只需最少的代码即可复制您的问题,我们可以轻松地将其复制粘贴到我们的 IDE 中并实际查看问题。您可以阅读 Minimal, Complete... 我在上面发布两次和Short, Self Contained, Correct Example (SSCCE) 的链接以了解我在说什么
-
非常感谢@Frakcool 的帮助,我会尽量简化我的代码,使其更短且易于执行!什么时候完成我会通知你的!谢谢 ! :)
标签: java swing drawing tiles isometric