I'm creating just a simple 2D game, and I'm having troubles understanding how to interact with other classes.
For example, before now I had all of the player pictures and coordinates stored inside the main loop of the program, but now I wish to take it outside of that, into a new class. and just refer to the player as like Player.whateverhere
To change it.. e.g OO programming.
GitHub Link for my full code.
public int x, y;
public boolean up, down, left, right, jump;
public Image offScreen;
public Graphics d;
public BufferedImage walkAnimations[] = new BufferedImage[5];
public BufferedImage background;
public BufferedImage player;
public void run() {
// Set Window Size
x = 100;
y = 100;
try {
// Import the images to use in the game
background = ImageIO.read(new File("img/background.jpg"));
walkAnimations[0] = ImageIO.read(new File("img/walkleft.png"));
walkAnimations[1] = ImageIO.read(new File("img/walkright.png"));
walkAnimations[2] = ImageIO.read(new File("img/walkup.png"));
walkAnimations[3] = ImageIO.read(new File("img/walkdown.png"));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
player = walkAnimations[3];
while (true) {
// Create the Gravity (If player !jumping; and if player is below or above the ground, then set player to ground.
if (!jump) {
if (y < Character.GROUND_LEVEL || y > Character.GROUND_LEVEL) {
y = Character.GROUND_LEVEL;
}
}
// If Keys are Pressed, Move the object
if (left) {
x -= 2;
} else if (right) {
x += 2;
} else if (up) {
y -= 2;
} else if (down) {
y += 2;
}
// Repaint the window to update the changes
repaint();
try {
// Make the thread sleep
Thread.sleep(20);
} catch (InterruptedException e) {
// Catch any errors with the Thread
e.printStackTrace();
}
}
}
(For the people not wanting to go to the trouble of visiting my GitHub for the code); i basically want to take the x/y, and images and stuff, and put them in a new class called "Player" but having a hard time making it work (e.g by doing this.player.y or player.y)
Simply put, if you look at Alpha 0.003 update, you can see it all in 2 classes.
I've tried to move all of the player stuff, into a new class/package called Player
So i'd like to be able to refer to it as say: player.x = 0; instead of doing x = 0;
and same with setting animations.
I'd like to just do this.player = walkAnimations[0]
But I don't understand how to do it properly. Is doing Player player = new Player(); wrong?
Basically, what you're doing is evil. Not "I murdered a baby and drank its blood evil", just the sort of affable friendly evil, like "I went to go pee during the commercial break".
So to answer the question, how should objects interact?
They should interact by sending each other messages.
In Java™ you interact by calling a method.
For instance, from a recent obligatory assignment me and another TA made, we have a SimplePlayer class with an illustrative example:
#Override
public void setDirection(Direction dir) {
this.faces = dir;
}
Thus if I want to tell the player object to face south, I can send it a message telling it to face south:
player.setDirection(Direction.SOUTH);
Now, you could ask, why not just call it like this?
p.faces = Direction.SOUTH;
Wouldn't that work?
Yes, in this and only this instance.
However, there might be other problems afoot when doing that sort of thing. Maybe the player object has to do something when he turns. Maybe it has to tell the sound system to play some noise. Maybe it has to add a number somewhere keeping track of how many steps you've taken. Maybe it has to send an email to the NSA. Who knows. And when you send the message, you don't have to care. the Player object is responsible for all of that. You are not.
This is the answer to the question "how should I interact with my objects?"
The answer to "Why doesn't what I do work?" is that the types don't match. You're trying to make the player be a BufferedImage object, which is not what you want.
"this.player = walkAnimations[0]"
this.player is a Player type variable.
walkAnimations[0] is a cell of a BufferedImage type array.
Therefore, the line will not compile.
Your question wasn't very clear, but I'll try to give you general guidelines on how to approach OO game programming, since I think essentially that's what your asking.
The key thing in Object Oriented Programming is that entities are represented by classes, and classes hold both the entity's state (the data related with it), and it's operations.
So for example a car can be represented by a Car class. This class will contain what characterizes a car - it's weight, it's color, it's price, etc - and also will contain what it can do - start, drive, stop, etc.
That means that every entity in your game (be it the player, monsters, whatever) should have it's own class. And that class should contain all of the entity's info - e.g. it's position on the screen (x,y), it's image - and also anything it can do (e.g. move on the screen, jump, fire a missile).
Here is a very simple Entity class for example in java-pseudocode. All game entity classes should inherit from this class:
class Entity{
private int x, y; // position on screen
private int dx, dy; // this is how much the entity moves each game-loop cycle.
private Image image;
private Rectangle boundingBox;
private int healthPoints;
public Entity(int x, int y){
// ths position of the thing is set through the constructor
this.x = x;
this.y = y;
dx = 0;
dy = 0;
healthPoints = 100;
Image = new Image(/*some file*/); // this is pseudo code.
boundingBox = new Rectangle(image); // pseudo code. A rectangle the size of the image.
}
public int getX(){ return x; }
public int getY() { return y; }
public Image getImage() { return image; }
// user input can affect this from the outside, to move the entity. or possibly an ai mechanism.
public void setDX(int dx){ this.dx = dx; }
public void setDY(int dy){ this.dy = dy; }
public void move(){
x += dx;
y += dy;
}
public Rectangle getBoundingBox(){ return boundingBox; }
}
Any object derived from this class will hold for itself all of it's data and operations.
Now, your gameloop control manages the entities from the outside and access their data - to move them, check for collisions, display their image, etc.
For example, a very simplified game class holding the game loop. In pseudocode (this is one approach, but it's good to start with something similar):
Class GameClass{
ArrayList entities;
public GameClass(){
entities = new ArrayList();
startGameLoop();
}
private void startGameLoop(){
while(true){ // this is bad, don't do this.
displayEntities(); // step 1 of gameloop.
moveEntities(); // step 2
checkForCollisions(); // step 3
}
}
private void displayEntities(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
displayImage(entity.getImage(),entity.getX(),entity.getY());
}
}
private void updateEntities(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
entity.move();
}
}
private void checkForCollisions(){
for(int i=0; i<entities.size(); i++){
Entity entity = entities.get(i);
for(int j=0; j<entities.size(); j++){
entities.remove(i);
}
}
}
}
This is missing ofcourse logic to get user input. What you can do is for example when the user presses the right arrow, set character.setDX(2), and left arrow will be character.setDX(-2). Remember that dx is the value added to x every cycle, when move() is invoked. This is one approach.
It's also missing tons of other things.
It's impossible to cover all of the basics in this one post. But just remember one thing:
All entities are encapsulated in classes, that hold their own data and operations. Have a central class to manage it all, contating a game loop. Also classes can have methods to interact with other classes.
Hope this helps.
EDIT: Also, here's a series of tutorials that helped me a lot when I was starting out.
Related
When I click screen, the Stop screen flashes on and then it goes back to the Walk screen. How do I get it to stay on the Stop screen until I click my mouse again?
boolean walk;
String textStop;
String textWalk;
float x; //positions
float y; //positions
void setup() {
size (400,500);
stop = false;
textStop = "STOP!";
textWalk = "WALK!";
}
void draw() {
background(0,255,0);
text(textWalk, x , y);
x = 150;
y = 250;
textSize(40);
}
void mousePressed() {
if (stop);{
background(255,0,0);
text(textStop, x , y);
}
} ```
Whenever you call background() you're clearing the screen.
Because this happens in draw() every frame is cleared and redrawn with textWalk (and textStop) only gets displayed when you click (for the short duration of the mousePressed() event).
You could use the walk variable for that. I see you use stop though it's not declared. They seem to be the opposite of each other in context so why not simpify and use just one. Sure you can code this if/else to toggle the value or you can simply use the !(logical not) operator:
void mousePressed() {
walk = !walk;
}
when walk is true render textWalk
otherwise (else) render textStop
something like:
if(walk){
text(textWalk, x , y);
}else{
text(textStop, x , y);
}
Here's a modified version of your code using the notes above:
boolean walk = true;
String textStop = "STOP!";
String textWalk = "WALK!";
float x = 150; //positions
float y = 250; //positions
void setup() {
size (400,500);
textSize(40);
}
void draw() {
if(walk){
background(0,255,0);
text(textWalk, x , y);
}else{
background(255,0,0);
text(textStop, x , y);
}
}
void mousePressed() {
walk = !walk;
}
(Notice I've moved a few lines: in general it's a good idea to initialise values that don't change once at the start of your program rather than continuously. In this basic example it doesn't really affect performance, but for more complex programs it's a good idea to keep track of what changes and what doesn't.
Also, it might help with other programs in the future to have a bit of separation between data that changes on events (e.g. walk on mouse pressed) and how that data gets rendered (e.g. using walk to render in draw())
So, for a coding assignment we have to make a tank game. I created a bullet class using this:
package com.MyName.battletanks;
import com.badlogic.gdx.graphics.g2d.Sprite;
public class Bullet {
public Sprite b;
public float vx = 0;
public float vy = 0;
public Bullet(Sprite temp) { b = temp;}
public void move() { b.translate(vx*3,vy*3);}
}
My variables are as follows:
Sprite Player1;
Sprite Player2;
ArrayList<Bullet> bullets;
Upon Clicking space it creates the bullet using this:
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
Bullet bullet = new Bullet(new Sprite(new Texture("Bullet1.png")));
bullet.b.setPosition(Player1.getX() + Player1.getWidth() / 2, Player1.getY() + Player1.getHeight() / 2);
float rotation = (float) Math.toRadians(Player1.getRotation());
bullet.vx = (float) Math.cos(rotation);
bullet.vy = (float) Math.sin(rotation);
bullets.add(bullet);
}
Now, here is my code for getting my tanks to pass through one side of the screen to another:
if (Player1.getX() > Gdx.graphics.getWidth()){
Player1.setX(-64f);
} else if(Player1.getX()<-64f){
Player1.setX(Gdx.graphics.getWidth());
}
if (Player1.getY() > Gdx.graphics.getHeight()){
Player1.setY(-64);
} else if(Player1.getY() < -64f){
Player1.setY(Gdx.graphics.getHeight());
}
Now, Player 1 is a sprite, however, the bullets are created using an arraylist and a self made bullet class. As a result, I cannot use the code for Player1 that I did for the bullet. SO, my question is, how can I get my bullet to pass to the other side of the screen?
You can use the modulo % operator to do something like this:
bullet.position.x %= Gdx.graphics.getWidth();
bullet.position.y %= Gdx.graphics.getHeight();
This isn't tested but it should work. Also, I noticed you're using 2 floats for your velocity, and you should really be using Vector2 because then you can easily scale and normalise it, which would be useful for a velocity.
It is really the same thing for the Player class and for the Bullet class since you have the same screen-space therefore you can reuse your current code by making it into a function that takes any sprite. This function would be defined as follows:
void fitOnScreen(Sprite sprite) {
if (sprite.getX() > Gdx.graphics.getWidth()){
sprite.setX(-64f);
} else if(sprite.getX()<-64f){
sprite.setX(Gdx.graphics.getWidth());
}
if (sprite.getY() > Gdx.graphics.getHeight()){
sprite.setY(-64);
} else if(sprite.getY() < -64f){
sprite.setY(Gdx.graphics.getHeight());
}
}
You would call this function on the Player as well as loop over every bullet, such as:
fitOnScreen(Player1);
for (Bullet b: bullets) fitOnScreen(b.b);
Well, what you are asking is how can you run through each instance of the bullets and change their properties.
I have to agree #Zac's algorithm would work fine for one bullet, but you need to put it in a loop to go through each and every bullet in order to be effective.
Here is an example of what you could do in your render() method of your game screen:
Iterator<Bullet> it = bullets.iterator();
while(it.hasNext()) {
Bullet bullet = it.next();
// Now you do the position correction
bullet.b.setPosition(bullet.b.getX()%Gdx.graphics.getWidth(), bullet.b.getY()%Gdx.graphics.getHeight());
}
Of course there are other ways of doing this, but this is probably the easiest way. You could also recycle the code you used for you player in this loop too.
Also, note that with this method, the more bullets, the more laggy the application becomes.
I am trying to animate a propeller in java and have come to this code:
int x = 0;
int y = 230;
int h = 40;
int i = 0;
int center = 250;
void setup() {
size(500, 400);
}
void draw () {
if (i == 0) {
if(y>200) {
ellipse(x, y, 20, h);
y--;
h+=2;
x+=1;
} else { i = i + 1; }
}
if (i == 1) {
if(y<=230) {
ellipse(x, y, 20, h);
y++;
h-=2;
x+=1;
} else { i = i + 1; }
}
if (i == 2) {
if(h<70) {
ellipse(x, y, 20, h);
y++;
h+=1;
x+=1;
} else { i = i + 1; }
}
if (i == 3) {
if(h>=40) {
ellipse(x, y, 20, h);
y--;
h-=1;
x+=1;
} else { i = 0; }
}
}
Is there a way of making this shorter, because I want to have 4 propellers and dont want to have so much code for this part.
You're going about this the wrong way. Very wrong, in fact. And the reason might be that you think you're doing this in "Java...in the program 'processing'"
The reason this sort of thinking is wrong is because it is equivalent to someone working with C++ saying, "I am creating classes in C using the program 'C++'". Processing is based on Java but it is a different programming language. If you're uncomfortable stretching the idea of Processing to that of a "programming language" then at least think of it as a framework...a framework that provides you with its own implementation of various tools that are put together to help with the creation of art using computers.
Now your thinking seems to have affected you in your particular case a lot. Just like Jose Gonzalez above suggested, you haven't even thought about rotate(), which is a function built into Processing. Tim B's suggestion about Objects is spot on as well but if you want to do things in Processing, try to do them the Processing way.
Here's a very quick sketch that I did for you to understand what I mean:
Prop myProp1;
Prop myProp2;
Prop myProp3;
Prop myProp4;
float angle;
void setup() {
size(500, 500);
background(255);
angle = 0;
fill(0);
ellipseMode(CENTER);
myProp1 = new Prop(50,50);
myProp2 = new Prop(75,75);
myProp3 = new Prop(100,100);
myProp4 = new Prop(125,125);
}
void draw() {
background(255);
angle = ((angle + 0.1) % 360);
myProp1.buildAndRotate(angle, width*3/4, height/4);
myProp2.buildAndRotate(angle, width/4, height/4);
myProp3.buildAndRotate(angle, width*3/4, height*3/4);
myProp4.buildAndRotate(angle, width/4, height*3/4);
}
class Prop {
int propX;
int propY;
int propW;
int propH;
Prop() {
propX = 0;
propY = 0;
propW = 50;
propH = 50;
}
Prop(int w, int h) {
propX = 0;
propY = 0;
propW = w;
propH = h;
}
void buildAndRotate(float angle, float moveToX, float moveToY) {
pushMatrix();
translate(moveToX, moveToY);
rotate(angle);
ellipse(propX, propY, propW, propH);
ellipse(propX+propW/2, propY+propH/2, propW, propH);
ellipse(propX-propW/2, propY+propH/2, propW, propH);
ellipse(propX+propW/2, propY-propH/2, propW, propH);
ellipse(propX-propW/2, propY-propH/2, propW, propH);
popMatrix();
}
}
Now, this is by no means meant to be the way to do things in Processing but it uses various tools provided by the programming language to do exactly what you want to do. Also, the Object Prop can be built in various different ways and my implementation is not supposed to be top-notch at all.
So what is going on? Well, run the sketch and you will see four propellers rotating on their own axes. I just put four of them there, you can delete them or add more as you please.
What the hell is a Prop made of? It is made of five ellipses. The concept, and this may be different than yours, is based on the idea of a ceiling fan (or other fans for that matter or even a prop engine). These fans have a circular thing in the middle to which all the blades are attached. The center circle rotates and that results in all the blades around it rotating with it. What you get in the end is a rotating fan.
How is this thing rotating? Similar to the fan analogy above, there is an ellipse in the middle that rotates around its center. There are four ellipses "attached" to it that rotate around this center ellipses. This gives the illusion of a prop rotating. What you have to understand is that in Processing rotate() rotates things around the origin. This is the reason the center ellipse starts at (0,0). Then later in buildAndRotate() it is translated using translate(). Using translate() does not move the object, it instead moves the origin resulting in the object moving with it, and then when you execute rotate() it rotates the object around its center. Since all the other ellipses are built around this center ellipse, they all rotate around it too (in actual implementation, they're just rotating around the origin, you can see that by removing the center ellipse). pushMatrix() and popMatrix() are used so all the translate() and rotate() commands don't affect everything else in the sketch and keep all movements and rotations applied to each object to that very object.
What else is going on? The rest is pretty simple (this is all very simple once you get the hang of it). Background is being cleared constantly which is a common animation technique to give the illusion of movement. You can delete the statement for background() from draw() but I wouldn't recommend it because it will leave a big black round circle after a while and you won't be able to see the props. The angle is being changed constantly in the draw() method which is why you see the props rotating. You can speed up or slow down the rotation by changing that + 0.1 value being added to angle if you want.
What else do I do? Read the reference (not the Java reference, Processing has its own reference) by using Google or following links such as: http://www.processing.org/reference/rotate_.html, http://www.processing.org/reference/pushMatrix_.html, http://www.processing.org/reference/popMatrix_.html, http://www.processing.org/reference/translate_.html, and many more.
Hope this helps and leave questions in the comment if you need clarification on something.
You need to think in terms of objects and iteration rather than writing out everything explicitly. You are correct that the code you have above contains a lot of un-needed duplication, which is a bad thing.
For a more complex case you would define each part of the propeller as an object. Have an array of parts within the Propeller object. Each time to do the draw you run through the list of objects and render each one out.
In this case it can be even simpler, just use a for loop.
At the top of your program define:
private static final int NUM_BLADES = 4;
Then you want a loop that looks something like this:
for (int i=0;i<360;i+=360/NUM_BLADES) {
// Draw elipse at current rotation position + the angle for this blade
}
Now you can change the number of blades just by changing the static define as well.
In the game i'm building, I have made a basic collision detection system.
My current method is explained below:
I workout where the player will be in the next step of the game:
double checkforx = x+vx;
double checkfory = y+vy;
I then check for a collision with blocks (1) in mapArray.
public static Boolean checkForMapCollisions(double character_x,double character_y){
//First find our position in the map so we can check for things...
int map_x = (int) Math.round((character_x-10)/20);
int map_y = (int) Math.round((character_y-10)/20);
//Now find out where our bottom corner is on the map
int map_ex = (int) Math.round((character_x+10)/20);
int map_ey = (int) Math.round((character_y+10)/20);
//Now check if there's anything in the way of our character being there...
try{
for(int y = map_y; y <= map_ey; y++){
for(int x = map_x; x <= map_ex; x++){
if (levelArray[y][x] == 1){
return true;
}
}
}
}catch (Exception e){
System.out.println("Player outside the map");
}
return false;
}
If true is returned {nothing}
If false is returned {Player physics}
I need the player to be able to land on a block and then be able to walk around but I cannot find and adequate tutorial for this.
Can someone give me an idea on how to run my collision detection and/or movement?
There are 2 parts to this question. Collision detection, meaning determining whether a volume is touching or intersecting another volume. The second is collision response. Collision response is the physics portion.
I'll cover collision detection here as that's primarily what you asked about.
Ddefine a class for the map like so:
int emptyTile = 0;
//this assumes level is not a ragged array.
public boolean inBounds(int x, int y){
return x>-1 && y>-1 && x<levelArray[0].length && y<levelArray.length;
}
public boolean checkForCollisions(Rectangle rectangle){
boolean wasCollision = false;
for(int x=0;x<rectangle.width && !wasCollision;x++){
int x2 = x+rectangle.x;
for(int y=0;y<rectangle.height && !wasCollision;y++){
int y2 = y+rectangle.y;
if(inBounds(x2,y2) && levelArray[y2][x2] != emptyTile){
//collision, notify listeners.
wasCollision=true;
}
}
}
}
Do not make your methods static. You probably want more than one instance of a level right? Static is for when you need to share state which remains constant across multiple instances of a class. Level data will surely not remain constant for every level.
Instead of passing in a coordinate, try passing in an entire rectangle. This rectangle will be the bounding box of your character (the bounding box is also sometimes referred to as AABB, which means Axis-aligned bounding box, just FYI in case you're reading tutorials online for this sort of thing.) Let your Sprite class decide what its bounding rectangle is, that's not the map class's responsibility. All the map should be used for is maybe rendering, and whether a rectangle is overlapping tiles which are not empty.
I am sorry for a very shitty explanation but here is my github code and it will help better.
https://github.com/Quillion/Engine
Just to explain what I do. I have character object (https://github.com/Quillion/Engine/blob/master/QMControls.java) and it has vectors and a boolean called standing. Every time boolean standing is false. Then we pass it to the engine to check for collision, if collision happens then standing is true and y vector is 0. As to x vector whenever you press any arrow keys you make the xvector of the object to whatever value you want. And in the update loop you displace the given box by the amount of speed.
I' m currently writing a simple 2D game in java from scratch (for learning purposes)
I want to control the rate at which a player can shoot. The method done there works, but it could be improved. The method is getting called if the user presses/holds the left mouse button. It works when the user is holding the button pressed, but when he/she releases the mouse button, waits (more then the rateOfFire time) and tries to shoot it may or may not work, because the roftC value isn' t getting updated when the player doesn' t shoot.
I tried then to put it into my update() method (which gets called 60 times a second). The problem still exists. I really have no idea how to solve this. Here is my code:
/**
* Used to control the rate of fire
*/
private int roftC = 0;
/**
* Shoot a Projectile
*/
protected void shoot(int x, int y, double dir) {
Projectile p = new Bomb(x, y, dir);
if (roftC % p.getRateOfFire() == 0) {
level.addProjectile(p);
}
if (roftC > 6000) {
roftC = 0;
}
roftC++; // Whether it is here or down there doesn' t make a diffrence
}
/**
*
*/
#Override
public void update() {
// roftC++;
}
One idea would be to introduce a minimum delay between shots. Something like this:
static final long MINIMUM_DELAY = 1000/30; // So we can have 30 shots per second
long lastShotTimestamp;
protected void shoot(int x, int y, double dir) {
long now = System.currentTimeMillis();
if (now - lastShotTimestamp > MINIMUM_DELAY) {
level.addProjectile(new Bomb(x, y, dir));
lastShotTimestamp = now;
}
}
Such an approach is actually close to the physics - a gun needs some time to reload between consecutive shots.