Hey Leute!
Ich schreibe nun schon seit knapp zwei Jahren mit Java „Plugins“ für das Spiel Minecraft und habe mich jetzt heute zum ersten Mal an einem eigenem Game versucht.
Die Basics kenne ich zwar aber mit Klassen wie JFrame oder JPanel habe ich noch nie vorher gearbeitet.
Nach einer kurzen Suche habe ich Quaxlis Tutorial gefunden. Einfach der Hammer!! Es ist ausführlich, detailiert und perfekt für Beginner. Vielen Dank dafür Quaxli (no homo)
Ich habe mich bis zu Seite 40 vorgearbeitet, bin dort allerdings auf ein Problem gestoßen…
Ich habe schon alles Mögliche probiert aber das will einfach nicht funktionieren…
Meine Klassen sehen im Moment so aus:
GamePanel
[spoiler]
package org.black_ixx.firstgame;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable, KeyListener{
private static final long serialVersionUID = 5989649846550855813L;
boolean game_running = true;
boolean started=false;
boolean once = false;
long delta = 0;
long last = 0;
long fps = 0;
boolean up = false;
boolean down = false;
boolean left = false;
boolean right = false;
int speed = 50;
Heli copter;
Vector<Sprite> actors;
public static void main(String[] args){
new GamePanel(800, 600);
}
public GamePanel(int w, int h){
this.setPreferredSize(new Dimension(w, h));
this.setBackground(Color.cyan);
JFrame frame = new JFrame("GameDemo");
frame.setLocation(100, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(this);
frame.add((this));
frame.pack();
frame.setVisible(true);
doInitializations();
}
private void doInitializations(){
last = 0;
actors = new Vector<Sprite>();
BufferedImage[] heli = this.loadPics("org/black_ixx/firstgame/pics/heli.gif", 4);
copter = new Heli(heli, 400, 300, 100, this);
actors.add(copter);
createClouds();
if (!once){
once=true;
Thread t = new Thread(this);
t.start();
}
}
@Override
public void run() {
while (game_running){
if (isStarted()){
computeDelta();
checkKeys();
doLogic();
moveObjects();
}
repaint();
try{
Thread.sleep(10);
}catch (InterruptedException e){
}
}
}
private void computeDelta(){
delta = System.nanoTime()- last;
last = System.nanoTime();
fps = ((long) 1e9/delta);
}
@Override
public void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.red);
g.drawString("FPS: "+Long.toString(fps), 20, 10);
if (!isStarted()){
return;
}
if (actors!=null){
for (Drawable draw : actors){
draw.drawObjects(g);
}
}
}
private BufferedImage[] loadPics(String path, int pics ){
BufferedImage[] anim = new BufferedImage[pics];
BufferedImage source = null;
URL pic_url = getClass().getClassLoader().getResource(path);
try{
source = ImageIO.read(pic_url);
}catch (IOException e){
}
for (int x = 0; x <pics; x++){
anim[x] = source.getSubimage(x*source.getWidth()/pics, 0, source.getWidth()/pics, source.getHeight());
}
return anim;
}
private void doLogic(){
for(Moveable mov : actors){
mov.doLogic(delta);
}
}
private void moveObjects(){
for (Moveable mov : actors){
mov.move(delta);
}
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN){
down = true;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
up = true;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = true;
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = true;
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN){
down = false;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
up = false;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = false;
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = false;
}
if (!up&!down){
copter.setVerticalSpeed(0);
}
if (!left&!right){
copter.setHorizontalSpeed(0);
}
if (e.getKeyCode() == KeyEvent.VK_ENTER){
if (!isStarted()){
doInitializations();
setStarted(true);
}
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
if (isStarted()){
setStarted(false);
}else{
setStarted(false);
System.exit(0);
}
}
}
@Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
private void checkKeys(){
if (up){
copter.setVerticalSpeed(-speed);
}
if (down){
copter.setVerticalSpeed(speed);
}
if (left){
copter.setHorizontalSpeed(-speed);
}
if (right){
copter.setHorizontalSpeed(speed);
}
}
public boolean isStarted(){
return started;
}
public void setStarted(boolean started){
this.started=started;
}
private void createClouds(){
BufferedImage[] ci = this.loadPics("org/black_ixx/firstgame/pics/todeswolke.png", 1);
for (int y=10; y < getHeight(); y+= 50)
{
int x = (int)(Math.random()*getWidth());
Cloud cloud = new Cloud(ci, x, y,1000, this);
actors.add(cloud);
}
}
}[/spoiler]
Sprite
[spoiler]
package org.black_ixx.firstgame;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
public abstract class Sprite extends Rectangle2D.Double implements Moveable, Drawable {
long delay;
GamePanel parent;
long animation = 0;
BufferedImage[] pics;
int currentpic = 0;
protected double dx;
protected double dy;
public Sprite(BufferedImage[] i, double x, double y, long delay, GamePanel p){
pics = i;
this.x=x;
this.y=y;
this.delay=delay;
this.width=pics[0].getWidth();
this.height=pics[0].getHeight();
parent = p;
}
@Override
public void drawObjects(Graphics g) {
g.drawImage(pics[currentpic], (int)x, (int)y,null);
}
@Override
public void doLogic(long delta) {
animation+=(delta/1000000);
if (animation > delay){
animation = 0;
computeAnimation();
}
}
@Override
public void move(long delta) {
if (dx!=0){
x+=dx*(delta/1e9);
}
if (dy!=0){
y += dy*(delta/1e9);
}
}
private void computeAnimation(){
currentpic++;
if (currentpic>=pics.length){
currentpic=0;
}
}
public void setVerticalSpeed(double d){
dy=d;
}
public void setHorizontalSpeed(double d){
dx=d;
}
public double getVerticalSpeed(){
return dy;
}
public double getHorizontalSpeed(){
return dx;
}
public void setX(double i){
x=i;
}
public void setY(double i){
y=i;
}
}[/spoiler]
Heli
[spoiler]
package org.black_ixx.firstgame;
import java.awt.image.BufferedImage;
public class Heli extends Sprite{
public Heli(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
super(i, x, y, delay, p);
}
@Override
public void doLogic(long delta){
super.doLogic(delta);
if (getX()<0){
setHorizontalSpeed(0);
setX(0);
}
if (getX()+getWidth() > parent.getWidth()){
setX((parent.getWidth()-getWidth()));
setHorizontalSpeed(0);
}
if (getY()<0){
setY(0);
setVerticalSpeed(0);
}
if (getY()+getHeight()>parent.getHeight()){
setY(parent.getHeight()-getHeight());
setVerticalSpeed(0);
}
}
}[/spoiler]
Cloud
[spoiler]
package org.black_ixx.firstgame;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Cloud extends Sprite {
final int SPEED = 20;
public Cloud(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
super(i, x, y, delay, p);
if (new Random().nextInt(2)==0){
setHorizontalSpeed(-SPEED);
}else{
setHorizontalSpeed(SPEED);
}
}
@Override
public void doLogic(long delta){
super.doLogic(delta);
if (getHorizontalSpeed()>0 && getX() > parent.getWidth()){
setX(-getWidth());
}
if (getHorizontalSpeed() < 0 && (getX() + getWidth()) <0){
setX(parent.getWidth()+getWidth());
}
}
}
[/spoiler]
Position der Wolken
Nun kommen wir zu dem Problem.
Ich konnte nirgendwo Quaxlis Wolken-Bild finden also habe ich mir einfach meine eigene Wolke zusammengebastelt.
Mit diesem Code werden die Wolken auch korrekt gespawnt:
BufferedImage[] ci = this.loadPics("org/black_ixx/firstgame/pics/todeswolke.png", 1);
for (int y=10; y < getHeight(); y+= 50)
{
int x = (int)(Math.random()*getWidth());
Cloud cloud = new Cloud(ci, x, y,1000, this);
actors.add(cloud);
}
}```
innerhalb der nächsten Millisekunden aber denkt das Spiel, dass die Wolken ausserhalb des sichtbaren Bereiches wären und bewegt sie zurück zu dem Rand des Fensters...
Ich habe stundenlang versucht das zu beheben. Ohne Erfolg
Was ich bisher herausgefunden habe (mithilfe von system.out.print(X variable / width of window):
``` int x = (int)(Math.random()*getWidth());
Cloud cloud = new Cloud(ci, x, y,1000, this);```
Hier werden sie bei einer zufälligen Position gespawnt. In der Mitte des Fensters.
Ich habe auch schon
``` int x = Random.nextInt(getWidth())```
versucht aber es kommt beidesmal auf das gleiche heraus. Das Problem liegt in einer anderen Methode.
Ich habe diesen Part hier genauer untersucht:
@Override
public void doLogic(long delta){
super.doLogic(delta);
#p1
if (getHorizontalSpeed()>0 && getX() > parent.getWidth()){
#p2
setX(-getWidth());
}
if (getHorizontalSpeed() < 0 && (getX() + getWidth()) <0){
#p3
setX(parent.getWidth()+getWidth());
}
}```
Anfangs als die Wolken gespawnt werden liegt X irgendwo zwischen 0 und 800.
Das erste Mal aber wenn doLogic ausgeführt wird ist X irgendeine riesige Zahl.
Diese Zahlen hier wurden geprintet: "684832.5869867401 685315.5869867401 685112.5869867401 684797.5869867401 685194.5869867401 685009.5869867401 684830.5869867401 685286.5869867401 "
X wird sofort wieder zurückgesetzt und von dem Moment an verhalten sich alle Wolken normal und fliegen quer über den Bildschirm.
Ich frage mich, wann und wieso X zu diesen Zahlen gesettet wird. Liegt das auch an einem Fehler in meinem Code? Ich habe den ganzen Code danach durchsucht…
Ich wäre wirklich dankbar über ein paar Antworten!