Libgdx Adding sprites to array and drawing them to screen - java

In this following code I have tried to create a coin class and make it so it is a sprite. I have tried to add all these sprites to an array and then draw the array to the screen. It does not produce any errors just does not print the graphic to the screen. I also wanted to know if i could use the sprite to test for collisions. I know my code is not very good as it is quite messy and im just trying to find a solution. Thanks
public class Gold extends Sprite {
private SpriteBatch batch;
private TiledMap map;
private Sprite sprite;
private Boolean isCollected;
public Gold(TiledMap map, Rectangle bounds, Texture gold) {
this.map = map;
sprite = new Sprite(gold);
sprite.setSize( bounds.width / MarioBros.PPM, bounds.height / MarioBros.PPM);
sprite.setPosition(bounds.x / MarioBros.PPM, bounds.y / MarioBros.PPM);
isCollected = false;
}
for (MapObject object : map.getLayers().get(5).getObjects().getByType(RectangleMapObject.class)) {
Rectangle rect = ((RectangleMapObject) object).getRectangle();
for(int i = 0; i < map.getLayers().get(5).getObjects().getCount() - 1; i++){
goldArray[i] = new Gold(map, rect, gold);
}
}
public void drawGold(TiledMap map){
for(int i = 0; i < map.getLayers().get(5).getObjects().getCount() - 1; i++){
goldArray[i].draw(batch);
}
}
In the render:
mapCreator.drawGold(map);
EDIT - I acted upon the first 2 suggestions and now the program outputs this error
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.alexcz.mariobros.Tools.MapCreator.<init>(MapCreator.java:77)
on this line goldArray[i] = new Gold(rect, gold);

I noticed that your Gold class, which is a Sprite, also has a reference to some other sprite, and that other sprite is the one you have set a region to, and is not the one you are drawing. Remove all references to another sprite in your Gold class.
Also, it would be better not to have a reference to a SpriteBatch or your TiledMap inside your Sprite. That introduces unnecessary coupling that could lead to bugs letter, or just make it harder to maintain your code as it gets more complicated.
public class Gold extends Sprite {
private boolean isCollected; //only use a primitive wrapper if you really need one
public Gold(Rectangle bounds, Texture gold) {
super(gold);
setSize( bounds.width / MarioBros.PPM, bounds.height / MarioBros.PPM);
setPosition(bounds.x / MarioBros.PPM, bounds.y / MarioBros.PPM);
isCollected = false;
}
//...
}

Because the constructor of the Gold class does not call the superclass's (Sprite) constructor, the default constructor is called implicitly. If you take a look at the documentation of the default constructor of Sprite you'll see it specifically notes that:
... Creates an uninitialized sprite. The sprite will need a texture region and bounds set before it can be drawn.
So that explains why nothing is being drawn. One way to fix this is to set a Texture Region. So in the Gold constructor add the following:
setRegion(gold);
There are different solution too. It all depends what you're trying to accomplish.

Related

Making a bullet pass opposite sides of the screen

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.

How to draw a sprite within the FitViewport view. libgdx

The basic question here is: how to ALWAYS keep your sprites within the Fitviewport? How to keep a reference to the view in order to have the proper coordinates as to where to draw?
I'm trying to spawn enemies into the gameplay screen. But this is handled by a FitViewport, and enemies and even the player can move outside the FitViewport on certain screen resolutions. So far the problem seems to be in the Y axis.
The FitViewport is made like this:
gameCamera = new OrthographicCamera();
gameCamera.setToOrtho(false);
gameViewport = new FitViewport(MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT,gameCamera);
gameViewport.setScreenBounds(0,0,MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT);
Then the camera position gets updated like this at the resize() method:
gameViewport.update(width,height); //not used when using the virtual viewport in the render method.
gameCamera.position.set(player.position.x + 200,player.position.y, 0);
Then the update() method calls the Player's own update() method which includes these lines:
//POSITION UPDATE
if (this.position.x<0) this.position.x=0;
if (this.position.x>Gdx.graphics.getWidth() - width) this.position.x= Gdx.graphics.getWidth() - width;
if (this.position.y<0) this.position.y = 0;
if (this.position.y>PlayScreen.gameViewport.getScreenHeight() - height) this.position.y = PlayScreen.gameViewport.getScreenHeight()- height;
Notice for the X axis I'm still using Gdx.graphics dimensions because I'm yet to make it work with PlayScreen.gameViewport.getScreenHeight() (gameViewport has been set to static for this purpose).
Also on enemy spawn (the problem related here is that they spawn outside of the screen Y in terms of what I see) I have this code inside the update() method of the Screen implementing all these viewports:
//Alien Spawn
if (System.currentTimeMillis() - lastZSpawn >= SpawnTimer){
count++;
lastZSpawn= System.currentTimeMillis();
for (int i=0;i<count;i++){
int x = Gdx.graphics.getWidth();
int y = random.nextInt((int)gameViewport.getScreenHeight() - Alien.height);
if (entities.size()<6){
entities.add(new Alien(new Vector2(x,y),1, alienImages,(float)((0))));
}
}
}
Also using gameViewport.getScreenHeight() here cause Gdx.graphics wasnt giving the correct result (it gave me the same issue really).
The render() method is correctly implemented in terms of the batch and applying the viewport:
MyGame.batch.setProjectionMatrix(gameCamera.combined);
gameViewport.apply();
MyGame.batch.begin();
for (int i = entities.size()-1; i>=0;i--){
entities.get(i).render();
}
You should never change the position of your player or enemies when resizing, that's why a viewport is for, remove all the code that do that first, to make your viewport work as you expected you need to create a new instance of camera passing the new viewport width and height when you resize, i prefer to make my camera static so i can acess its atribbutes from everywhere i want, you should do something like this:
public static OrthographicCamera update(int width,int height){
instance = new OrthographicCamera(width, height);
instance.setToOrtho(false);
return instance;
}
The answer to my problem is posted by myself in another question of mine which was also driven by a confusion in the implementation of FitViewports and using WorldWidth and WorldHeight properties as coordinates of reference when drawing objects into the game, and also correctly setting the camera position taking these values into consideration aswell.
The answer is here even though its text and not code and its mainly what i already wrote in this very post. FitViewport doesnt scale properly Libgdx

Is it possible to create a simple animation just with translation and rotation in libGDX?

I'm trying to develop a very simple game using libGDX with boxes (so 3D game) moving and rotating.
I have almost everything ready, but I'm not able to animate my boxes. I mean, when I touch the screen, I'd like my cube to move to the right by rotating 90 degrees and translating 1 (unit) to the right. As result, the right side of the box will be the new base, the old base will be in the left side, and the box is moved to the right.
So, the question is: now that I have the move set correctly (I or at least I hope so), but change is applied immediately; so how can I see the animation between first position and second position ?
Only reference to animation for 3D objects in documentation is about using obj files from blender (and similar), and for movement I need I do not consider it necessary.
Can anybody provide me some help? Thanks in advance!!
You can do that something like this:
public static class YourAnimation {
public ModelInstance instance;
public final Vector3 fromPosition = new Vector3();
public float fromAngle;
public final Vector3 toPosition = new Vector3();
public float toAngle;
public float speed;
public float alpha;
private final static Vector3 tmpV = new Vector3();
public void update(float delta) {
alpha += delta * speed;
if (alpha >= 1f) {
alpha = 1f;
// TODO: do whatever you want when the animation if complete
}
angle = fromAngle + alpha * (toAngle - fromAngle);
instance.transform.setToRotation(Vector3.Y, angle);
tmpV.set(fromPosition).lerp(toPosition, alpha);
instance.transform.setTranslation(tmpV);
}
}
YourAnimation animation = null;
void animate(ModelInstance instance) {
animation = new YourAnimation();
animation.instance = instance;
animation.instance.transform.getTranslation(animation.fromPosition);
animation.toPosition.set(animation.fromPosition).add(10f, 10f, 10f);
animation.fromAngle = 0;
animation.toAngle = 90f;
animation.speed = 1f; // 1 second per second
animation.alpha = 0;
}
public void render() {
final float delta = Math.min(Gdx.graphics.getDeltaTime(), 1/30f);
if (animation != null)
animation.update(delta);
// render model as usual etc.
}
Ofcourse this is just a quick example. The actual implementation will vary depending on the use case. For example you could also extend ModelInstance and keep track of the animation in there. Because it is very specific to the use-case, but very simple to implement, it is usually not worth using tools (like the Universal Tween Engine)
Here is another example I recently wrote for my latest tutorial, perhaps it helps as well. It rotates and moves the cards in this video.

Touchevents using libgdx and scene2d

i am currently going through some tutorials on android development and libgdx/scene2d atm but got stuck on the touchevents:
For starters i am implementing a board game. By now i managed to draw the board and some meeples (player tokens) as well as adjust their position to the correct coordinates. When i started using libgdx i also successfully implemented a first test to see if the touchinput is working. The test looked something like this:
if(Gdx.input.isTouched()){
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
stone.x=touchPos.x - stone.width/2;
stone.y=touchPos.y - stone.height/2;
By now i rearranged my code to use scene2d because of some very convenient features. However it seems like i can't manage to get touchevents to work with scene2d despite i followed some rather simple tutorials. Since some of them reference some recent major changes to scene2d, i wondered if those tutorials might be outdated and hope you guys can help me fix my problem :) I will try and only paste the relevant parts of my code in here to present a minimal example of my non-working code.
Of course i am also glad about any hints on how to "improve" my code in general since i am still learning and probably break some conventions here ^^
Lets start with my actor-class:
//deleted: imports
public class Player extends Actor{
private int xval; //x Position of the Player on the board
private int yval; //y Position of the Player on the board
private Color color;
private TextureRegion playerTexture;
//deleted: some variables that are unimportant for touchevents
public Player(int x, int y, TextureRegion texture){
//initializing some Variables with default values
this.xval = x;
this.yval = y;
color = new Color(1,1,1,1);
playerTexture = texture;
this.setTouchable(Touchable.enabled); //this should be set by default, but i added it just to be sure.
//setBounds seems to be necessary in addition to the "touchable" flag to be able to "grab" or "touch" Actors.
//getX and getY are methods of the actor class which i do not overwrite. I use distinct getters and setters to modify the Variables xval and yval.
setBounds(getX(), getY(), playerTexture.getRegionWidth(), playerTexture.getRegionHeight());
//now i implement the InputListener. This doesnt seem to get called, since the "touch started" message doesn't appear in LogCat.
//In Theory the testevent should move the meeple one space to the right.
//To do that i call event.getTarget() which returns the actor of which the TouchDown is called. I then cast it to my Actor class (Player) and set xval to xval+1.
this.addListener(new InputListener(){
public boolean touchDown(InputEvent event, float x, float y, int pointer, int buttons){
Gdx.app.log("Example", "touch started at (" + x + ", " + y + ")");
((Player)event.getTarget()).setXf(((Player)event.getTarget()).getXf()+1);
return true;
}
});
}
//This is my draw method, which just sets the color of my meeple and then draws it to its current position.
public void draw(Batch batch, float alpha){
batch.setColor(color);
batch.draw(playerTexture, getX(), getY());
}
//deleted: several getters and setters
}
So if i got it right, the gdx inputProcessor is managing all inputs. If i set the Gdx InputProcessor to the stage which contains the actors, it will in case of an event (for example a touchDown) call the inputProcessors of all actors on the stage. Thus since i just added one to my class which includes a touchDown, this should handle the touchDown event in the case that the actor is actually touched. The hitbox to verify that is set by the statement "setBounds" in my Player class.
I implemented this in my ApplicationListener class:
//deleted: imports
public class QuoridorApplicationListener implements ApplicationListener{
Stage stage;
OrthographicCamera camera;
//deleted: several variable declarations.
//deleted: constructor - this only fills some of my variables with values depending on game settings.
#Override
public void create() {
//set stage - since "stage = new Stage(800,400,false)" doesn't seem to work anymore, i did set up a viewport manually. If you got suggestions how to this straightforward, please leave a note :)
Gdx.app.log("Tag", "game started");
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
Viewport viewport = new Viewport() {};
viewport.setCamera(camera);
stage = new Stage(viewport);
Gdx.input.setInputProcessor(stage); //like i described above, this should forward incoming touchevents to the actors on the stage.
//deleted: setting textures, music, setting up the board (the boardtiles are actors which are added to the stage)
//deleted: setting TextureRegion, player colors, player positions and player attributes.
for (int i = 0; i < playercount; i++){
stage.addActor(players[i]);
}
}
//deleted: dispose(), pause(), resume(), resize() methods.
#Override
public void render() {
camera.update();
for (int i=0; i< playercount; i++){
players[i].setX(/*Formula to determine x-coordinates from Player.xval*/);
players[i].setY(/*Formula to determine x-coordinates from Player.yval*/);
}
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
}
I can't figure out what i am missing to get the touchevents working. So i hope you can help me :) Thanks a lot in advance!
I think your problem originates from your manual creation of a Viewport object, which handles the unprojecting internally.
Instead of
stage = new Stage(800,400,false);
you should use
stage = new Stage();
To resize the the viewport to the correct size once a resize event occurs you need to call
stage.getViewport().update( newWidth, newHeight )
in your ApplicationListener's resize method.

SWT - Drawing a Moving Image Over Tiles

I've been experimenting with different ways of moving a image over a grid of tiles for a game, but I've been unable to get a working implementation.
First I tried using a grid layout to hold a bunch of Tiles that extended Canvas and drew themselves. This drew the tiles nicely, however it seems that I am unable to draw my Player object on top of them. Originally, the Player also extended Canvas and I intended to have the widget on top of the tiles. It seems like this is impossible.
I then tried to have the Tile simply extend nothing, and just hold the image. I then hold each Tile in a 2D array and draw each Tile by a nested for loop, using the int from the for loop, multiplied by the image size, to draw Tile's Image. I put this code in a PaintListener inside of my constructor for my Map class that extended Canvas and dropped my Map onto my Shell in a Fill layout, but the PaintListener never gets called (I tested with a print statement).
What implementation could I use to draw the Tiles at the start of the game, then allow me to control the movement of my Player image?
I did something similar.
Using a PaintListener I get the calls when the Widget needs to be repainted. In my paint function, I loop over a tile array (wrapped in a World class) and draw all tiles. Afterwards I use the same technique with a worldObjects array/class:
public class WorldWidget extends Canvas {
WorldWidget() {
addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
WorldWidget.this.paintControl(e);
}
});
}
protected void paintControl(PaintEvent e) {
GC gc = e.gc;
for (short y = 0; y < world.getHeight(); y++) {
for (short x = 0; x < world.getWidth(); x++) {
final ITile tile = world.getTile(x, y);
final Image image = ImageCache.getImage(tile);
gc.drawImage(image, x * tileSize, y * tileSize);
}
}
// Here is used a similar loop, to draw world objects
}
}
This is obviously a condensed code example, as the class is part of an editor and reacts on mouse clicks and movement amongst other things.
When I did a tile based simulation while ago I did it this way:
I had 2 layers of the tile map - one for the terrain and second for the units.
The map itself was represented by a JPanel.
So roughly you got this for the JPanel:
public void paintComponent(Graphics graphics) {
// create an offscreen buffer to render the map
if (buffer == null) {
buffer = new BufferedImage(SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);
}
Graphics g = buffer.getGraphics();
g.clearRect(0, 0, SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT);
// cycle through the tiles in the map drawing the appropriate
// image for the terrain and units where appropriate
for (int x = 0; x < map.getWidthInTiles(); x++) {
for (int y = 0; y < map.getHeightInTiles(); y++) {
if (map.getTerrain(x, y) != null) {
g.drawImage(tiles[map.getTerrain(x, y).getType()], x * map.getTILE_WIDTH(), y * map.getTILE_HEIGHT(), null);
}
}
}
if (map.getSimulationUnits() != null) {
for (Unit unit : map.getSimulationUnits()) {
g.drawImage(tiles[unit.getUnitType()], (int) Math.round(unit.getActualXCor() * map.getTILE_WIDTH()), (int) Math.round(unit.getActualYCor() * map.getTILE_HEIGHT()),
null);
}
}
// ...
// draw the buffer
graphics.drawImage(buffer, 0, 0, null);
}
Logic:
private Terrain[][] terrain = new Terrain[WIDTH][HEIGHT];
/** The unit in each tile of the map */
private Unit[][] units = new Unit[WIDTH][HEIGHT];
Then you have your game loop where you update the position of the units and other things, basically render() and update() the game. Check the links I've provided below.
NOTE
Since you are making a simple game this post about making game loops will be definitely useful for you. This hopefully also answer your question about moving the object on the map.
This site will be also very helpful since you will probably need to detect collision at some point too.

Categories

Resources