我尝试重玩“Gravity Guys”游戏,在这个游戏中玩家只有在接触表面时才能改变重力。问题是它允许我在空气中改变重力。尝试给你举个例子来解释这个问题。想象一下玩家沿着方块行走,方块是半高的,完成的方块,如果我不按空格键改变重力,重力的玩家开始下落,但此时变量布尔值canswitchgravity设置为true,因为它之前在方块上行走时被激活,然后玩家可以跳跃,我不想要这个
public class Player extends GameObject {
private float width=32 , height=64;
private float gravity = 0.1f; //il suffisso f serve per indicare che è float
private final float MAX_SPEED=10;
private Handler handler;
public Player(float x, float y, Handler handler, ObjectId id){
super(x, y, id);
this.handler=handler;
}
public void tick(ArrayList<GameObject> object) {
//Aggiorna la posizione del giocatore in base alla sua velocità.
x+=velX;
y+=velY;
if(gravityInverted){
velY+= -gravity; //inverti gravità --> sale
}else
velY+=gravity; //gravità normale --> scende
if(velY> MAX_SPEED){
velY=MAX_SPEED; //limito la velocità
} else if (velY < -MAX_SPEED) {
velY = -MAX_SPEED;
}
x+=3;
Collision(object);
}
private void Collision(ArrayList<GameObject> objects){
for(int i=0; i< handler.object.size(); i++){
GameObject tempObject= handler.object.get(i);
if(tempObject.getId() == ObjectId.Block){
if (getBoundsTop().intersects(tempObject.getBoundsBot())) {
y=tempObject.getY() +32;
velY = 0;
canSwitchGravity=true;
}
if (getBoundsBot().intersects(tempObject.getBoundsTop())) { //intersects verifica se 2 rettangoli si intersecano
y=tempObject.getY() - height;
velY = 0;
canSwitchGravity=true;
}
//Right
if (getBounds().intersects(tempObject.getBounds())) {
x=tempObject.getX() -32;
}
//left
if (getBoundsLeft().intersects(tempObject.getBounds())) {
x=tempObject.getX() +32;
}
}
}
}
public void render(Graphics g) {
g.setColor(Color.blue);
g.fillRect((int)x, (int) y,(int)width, (int)height);
}
//definisco i rettangoli di collisione in diverse direzioni (alto, basso, sinistra e destra) per il giocatore
public Rectangle getBoundsBot() {
return new Rectangle((int)(x+(width/2)-(width/2)/2), (int) (y+(height/2)) ,(int)width/2, (int)height/2 );
}
public Rectangle getBoundsTop() {
return new Rectangle((int)(x+(width/2)-(width/2)/2), (int) y,(int)width/2, (int)height/2 );
}
public Rectangle getBounds() {//right
return new Rectangle((int)(x+width-5), (int) y+5,(int) 5, (int)height-10 );
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)x, (int) y+5,(int) 5, (int)height-10 );
}
}
public class Game extends Canvas implements Runnable{
private boolean running = false;
private Thread thread;
public static int HEIGHT,WIDTH;
private Menu menu;
public State gameState = State.Menu;
public int score = 0;
Handler handler;
Camera cam;
Random rand = new Random();
public synchronized void start(){
if(running) //se è true il metodo ritorna immediatamente e non fa nulla (il gioco è già in esecuzione).
return;
running=true;
thread = new Thread(this); // crea un nuovo thread che eseguirà il codice del metodo run() della tua classe Game(this si riferisce a game poichè thread accetta oggetto runnable e game implemetna runnable).
thread.start(); //chiama il metodo run
}
private void init(){
WIDTH= getWidth();
HEIGHT = getHeight();
handler = new Handler();
cam = new Camera(0,0);
menu = new Menu(this, handler, cam);
this.addKeyListener(new KeyInput(handler));
this.addMouseListener(menu);
}
public void run(){ //aggiungo ciclo di gioco
init();
this.requestFocus(); // Garantisce che la finestra di gioco riceva il focus per permettere l'interazione dell'utente tramite tastiera e mouse con la finestra di gioco.
long lastTime = System.nanoTime();
double amountOfTicks = 60.0; //il gioco aggiorna la sua logica 60 volte al secondo
double ns = 1000000000/amountOfTicks; // è il numero di nanosecondi che devono passare tra un tick e l'altro per ottenere esattamente amountOfTicks tick al secondo
double delta = 0; //delta è essenziale per mantenere il gioco aggiornato a un ritmo costante di 60 volte al secondo
long timer = System.currentTimeMillis();
int updates = 0;
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += ( now - lastTime)/ ns; // tiene traccia del tempo trascorso tra due esecuzioni del ciclo, per assicurarsi che gli aggiornamenti logici (tick) avvengano esattamente a 60
lastTime = now ; //now - lastTime calcola quanto tempo è passato dall'ultima iterazione del ciclo
while (delta >=1) { //Questo valore viene diviso per ns (cioè il tempo tra un tick e l'altro), per ottenere un valore che rappresenta quante frazioni di tick sono trascorse
tick();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer+=1000;
System.out.println("FPS: " + frames + " TICKS: " + updates);
frames=0;
updates=0;
}
}
}
//metodo che è responsabile dell'aggiornamento della logica del gioco (es. movimento dei personaggi, calcolo delle collisioni, ecc.)
private void tick(){
handler.tick();
if (gameState == State.Game) {
cam.tick();
score ++;
for (int i = 0; i < handler.object.size(); i++) {
GameObject tempObject = handler.object.get(i);
if (tempObject.getId() == ObjectId.Player) {
Player player = (Player) tempObject; // Ottieni l'istanza del player
// Controlla se il player è fuori dallo schermo
if (player.getY() > (cam.getY() + Game.HEIGHT+64) || player.getY() < cam.getY()-64 || player.getX() < (-1 *cam.getX()-32) ) {
gameState = State.End;
}
}
}
} else if (gameState == State.Menu || gameState == State.End) {
menu.tick();
}
}
//metodo per disegnare sullo schermo usando una strategia di buffering per migliorare la fluidità e ridurre lo sfarfallio.
private void render(){
BufferStrategy bs = this.getBufferStrategy();//Recupera la strategia di buffering corrente
if(bs== null){ //Se non esiste una strategia di buffering, ne crea una
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d=(Graphics2D) g;//trasformo g in g2d perchè graphics2d fornisce funzioni + avanzate come la traslazione
////////////////////////////
//Draw here
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
if (gameState == State.Game) {
g2d.translate(cam.getX(), cam.getY());//begin of cam
//translate() sposta l'intera area di disegno in base alle coordinate della telecamera.
handler.render(g);
g.drawString("Score: "+ score, (int)(-cam.getX()+64),(int) (-cam.getY()+32));
g2d.translate(-cam.getX(), -cam.getY());//end of cam
//Se non usassi g2d.translate(-cam.getX(), -cam.getY()), la traslazione si accumulerebbe ad ogni ciclo di rendering, ciò che disegnerei nei cicli successivi sarebbe ulteriormente spostato di una certa quantità rispetto alla posizione corrente
}else if (gameState == State.Menu || gameState == State.Help || gameState == State.End) {
menu.render(g);
}
g.dispose();
bs.show();
}
public void LoadImageLevel( BufferedImage image){
int w = image.getWidth();
int h = image.getHeight();
System.out.println("width, height: " + w + " "+ h);
for(int xx =0; xx<h; xx++){
for(int yy=0; yy < w; yy++){
int pixel = image.getRGB(xx, yy);
Color pixelColor = new Color(pixel);
if( pixelColor.equals(Color.white)){
handler.addObject(new Block(xx*32, yy*32, ObjectId.Block));
}
if( pixelColor.equals(Color.blue)){
handler.addObject(new Player(xx*32, yy*32, handler,ObjectId.Player));
}
//moltiplico per 32 perchè 1 pixel rappresenta un blocco 32x32
}
}
}
public void setUpGame(){
gameState = State.Game;
BufferImageLoader loader = new BufferImageLoader();
LoadImageLevel(loader.loadImage("/src/res/level.png"));
}
public void restart(){
handler.clearObjects();
cam.setX(0);
cam.setY(0);
setUpGame();
gameState = State.Game;
score=0;
}
public static void main(String[] args) {
new Window(800, 600, "prototype",new Game());
}
}
public class KeyInput extends KeyAdapter{
Handler handler;
public KeyInput(Handler handler){
this.handler=handler;
}
public void keyPressed(KeyEvent e){
int key=e.getKeyCode();
for(int i =0 ; i<handler.object.size(); i++){
GameObject tempObject= handler.object.get(i);
if (tempObject.getId() == ObjectId.Player) {
if (key == KeyEvent.VK_SPACE && ((Player) tempObject).canSwitchGravity ) {
tempObject.setGravityInverted(!tempObject.isGravityInverted());
((Player) tempObject).canSwitchGravity = false;
}
}
}
if(key== KeyEvent.VK_ESCAPE){
System.exit(1);
}
}
}
public abstract class GameObject{
protected float x,y;
protected float velX=0,velY=0;
protected ObjectId id;
protected boolean gravityInverted=false;
protected boolean canSwitchGravity = false;
public GameObject(float x, float y, ObjectId id){
this.x = x;
this.y=y;
this.id = id;
}
public abstract void tick(LinkedList <GameObject> object);
public abstract void render(Graphics g);
public abstract Rectangle getBounds();
public abstract Rectangle getBoundsBot();
public abstract Rectangle getBoundsTop();
public float getX(){
return x;
}
public float getY(){
return y;
}
public void setX(float x){
this.x = x;
}
public void setY(float y){
this.y = y;
}
public float getVelX(){
return velX;
}
public float getVelY(){
return velY;
}
public void setVelX(float velX){
this.velX=velX;
}
public void setVelY(float velY){
this.velY=velY;
}
public boolean isGravityInverted(){
return gravityInverted;
}
public void setGravityInverted(boolean gravityInverted){
this.gravityInverted=gravityInverted;
}
public ObjectId getId(){
return id;
}
}
public class Block extends GameObject{
public Block(float x, float y, ObjectId id){
super(x, y, id);
}
public void tick(ArrayList <GameObject> object){
}
public void render(Graphics g){
g.setColor(Color.white);
g.drawRect((int)x,(int) y, 32, 32);
/*Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.red);
g2d.draw(getBoundsBot());
//g2d.draw(getBoundsBot());
g2d.draw(getBounds());*/
}
public Rectangle getBoundsTop(){//top
return new Rectangle((int) x, (int) y, 32,10);
}
public Rectangle getBounds(){
return new Rectangle((int) x, (int) (y), 32, 32);
}
public Rectangle getBoundsBot(){
return new Rectangle((int) x, (int) (y+22), 32,10);
}
}
public class Handler {
public ArrayList<GameObject> object = new ArrayList<GameObject>(); //contiene tutti gli oggetti di gioco attivi.
private GameObject tempObject; //riferimento temporaneo a ciascun oggetto della lista mentre si itera su di essa, posso farne anche a meno
/*
il metodo tick viene chiamato ad ogni ciclo di gioco per aggiornare lo stato di ciascun oggetto
scorre la lista di tutti gli oggetti di gioco
Per ogni oggetto, chiama il suo metodo tick() (che è definito nella classe concreta che estende GameObject),
*/
public void tick(){
for(int i =0; i< object.size(); i++){
tempObject = object.get(i);
tempObject.tick(object);
}
}
/*
Scorre la lista degli oggetti, come nel metodo tick.
Per ogni oggetto, chiama il suo metodo render(Graphics g) per disegnare l'oggetto
*/
public void render (Graphics g){
for(int i = 0; i < object.size(); i++){
tempObject = object.get(i);
if (tempObject!= null) {
tempObject.render(g);
}
}
}
public void addObject(GameObject object){
this.object.add(object);
}
public void removeObject( GameObject object){
this.object.remove(object);
}
public void clearObjects(){
object.clear();
}
}
这些是这个问题需要的主要类
问题似乎在于,即使玩家在空中,
canSwitchGravity
仍然是true
。发生这种情况是因为,一旦在与表面碰撞期间将其设置为 true
,当玩家不再接触表面时,它就不会重置为 false
。
要解决此问题,您需要在碰撞检测开始时将
canSwitchGravity
重置为 false
。这可确保当玩家实际与表面碰撞时,canSwitchGravity
才为 true
。
以下是修改
Collision
类中的 Player
方法的方法:
private void Collision(ArrayList<GameObject> objects){
canSwitchGravity = false;
for(int i = 0; i < handler.object.size(); i++){
GameObject tempObject = handler.object.get(i);
if(tempObject.getId() == ObjectId.Block){
if (getBoundsTop().intersects(tempObject.getBoundsBot())) {
y = tempObject.getY() + 32;
velY = 0;
canSwitchGravity = true;
}
if (getBoundsBot().intersects(tempObject.getBoundsTop())) {
y = tempObject.getY() - height;
velY = 0;
canSwitchGravity = true;
}
if (getBounds().intersects(tempObject.getBounds())) {
x = tempObject.getX() - 32;
}
if (getBoundsLeft().intersects(tempObject.getBounds())) {
x = tempObject.getX() + 32;
}
}
}
}