[Slick2D] Strange collision glitch.

Tht wouldn’t be it, because even if I don’t have shutdown there… Just a println it still lags

My guess is that some event handling or something is preventing the VM from exiting, aka while key is being held down for movement, but that’s just a hunch.
[/quote]
Yeah, that’s what I was thinking. The gc.exit(); is firing as soon as the key is released, I had a similar problem in my game. That’s one of the reasons I added input.clearKeyPressedRecord(); to my keystrokes. You can still “hold down” keys but as the game is polling it’ll allow certain actions to happen that otherwise may be blocked out. From how I understand it, it’s basically dumping the key input buffer constantly, allowing actions to happen before the key is put back in the buffer.

There may be a much better way to do it, but it works like a champ for me. I’ve had no reason to do it differently.

[quote]It only happens when you walk directly towards the zombie though. Otherwise its quite responsive.
[/quote]
Can you elaborate on this? If collisions work while the player is not moving, but “lags” when the player is moving, then i think the problem lies in the player. I can come up with a lot of different things that can be wrong if this is the case, but instead of speculating would it be possible to post a little more code on the player class.

Ya sure thing I’ll give the entire class on second

Entire Player Class:
WARNING: Quite messy


package com.pickens.objects;

import java.awt.Font;

import org.lwjgl.input.Mouse;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.TrueTypeFont;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.state.StateBasedGame;

import com.pickens.objects.controllers.ArcadeCounter;
import com.pickens.objects.controllers.BulletController;
import com.pickens.objects.controllers.ZombieController;
import com.pickens.src.Game;

public class Player {

	//TODO: Fix Animations
	
	float x = 640/2-28;
	float y = 256-128;
	
	//TODO: Change this to more reasonable number when ammo drops are available
	
	int ammo = 100;
	
	double velX = 0;
	double velY = 0;
	
	int[] duration = {150, 150};
	
	public Rectangle bounds;
	
	BulletController bm;
	ZombieController zc;
	ArcadeCounter ac;
	Input input;
	
	Animation lt;
	Animation rt;
	Animation rti;
	Animation lti;
	Animation main;
	
	//TODO: Better Gun Management when there are more weapons!
	Image gun;
	boolean shouldFire = false;
	
	boolean moving = false;
	
	boolean l = false;
	boolean r = true;
	
	boolean ml = false, mr = false;
	
	int xpos;
	
	Font awtFont;
	TrueTypeFont font;
	
	public Player(ArcadeCounter ac, BulletController bm, ArcadeCounter ac1, ZombieController zc) throws SlickException {
		this.ac = ac1;
		this.bm = bm;
		this.zc = zc;
		init();
	}
	
	public void init() throws SlickException {
		Image[] lt = {new Image("res/ZN_Left1.png"), new Image("res/ZN_Left2.png")};
		Image[] rt = {new Image("res/ZN_Right1.png"), new Image("res/ZN_Right2.png")};
		Image[] lti = {new Image("res/ZN_Left Idle.png"), new Image("res/ZN_Left Idle.png")};
		Image[] rti = {new Image("res/ZN_Right Idle.png"), new Image("res/ZN_Right Idle.png")};
		
		this.lt = new Animation(lt, duration, false);
		this.rt = new Animation(rt, duration, false);
		this.rti = new Animation(rti, duration, false);
		this.lti = new Animation(lti, duration, false);
		
		main = this.rti;
		
		gun = new Image("res/Pistol.png");
		
		awtFont = new Font("Arial", Font.BOLD, 12);
		font = new TrueTypeFont(awtFont, false);
		
		bounds = new Rectangle((int)x, (int)y, main.getWidth(), main.getHeight());
		
		ac.start();
	}
	
	public void render(Graphics g) throws SlickException {
		//other objects' renders
		main.draw((int) x, (int) y);
		bm.render(g);
		
		//This objects render
		if(r)
			g.drawImage(gun, (int) x + 28, (int) y + 20);
		if(l)
			g.drawImage(gun.getFlippedCopy(true, false), (int) x - 10, (int) y + 20);
		else
			g.drawImage(gun, (int) x + 28, (int) y + 20);
		g.setColor(Color.white);
		g.setFont(font);
		g.drawString("Ammo: " + ammo, 12, 12);
		g.setColor(Color.red);
		
		g.draw((Shape)getBounds());
	}
	
	public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException, InterruptedException {
		//Other objects' updates
		main.update(delta);
		bm.update(gc, sbg, delta);
		
		input = gc.getInput();
		
		xpos = Mouse.getX();
		
		if(xpos < x) {
			ml = true;
			mr = false;
		}
		if(xpos > x) {
			mr = true;
			ml = false;
		}
		
		if(moving)
			x += velX;
		
		//Important Area
		if(ac.delete) {
			collision();
			movement();
			fire();			
		}
	}
	
	public void movement() {
		if(input.isKeyDown(Input.KEY_RIGHT)) {
			moving = true;
			l = false;
			r= true;
			main = rt;
			main.start();
			velX = .13;
		}else
		if(input.isKeyDown(Input.KEY_LEFT)) {
			moving = true;
			r = false;
			l = true;
			main = lt;
			velX = -.13;
		}else{
			if(main == rt && main != rti && main != lti) {
				main = rti;
				l = false;
			}else if(main == lt && main != rti && main != lti){
				main = lti;
				r = false;
			}
			moving = false;
			velX = 0;
		}
	}
	
	public void fire() throws SlickException {
		//Disable gun if ammo == 0
		if(ammo == 0) {
			shouldFire = false;
		}
		
		//Shooting
		if(input.isMouseButtonDown(0)) {
			if(shouldFire && ml) {
				if(xpos > x) {
					bm.addBullet(new Bullet(x-2, y + 23,0,bm));
					ammo--;
					shouldFire = false;
					return;
				}else{
					if(moving)
						main = lt;
					else
						main = lti;
					r = false;
					l = true;
					bm.addBullet(new Bullet(x-2, y + 23,0,bm));
					ammo--;
					shouldFire = false;
					return;
				}
			}	
			if(shouldFire && mr) {
				if(xpos < x) {
					bm.addBullet(new Bullet(x+36, y + 23,1,bm));
					ammo--;
					shouldFire = false;
					return;
				}else{
					if(moving)
						main = rt;
					else
						main = rti;
					l = false;
					r = true;
					bm.addBullet(new Bullet(x+36, y + 23,1,bm));
					ammo--;
					shouldFire = false;
					return;
				}
			}
		}else{
			shouldFire = true;
		}
	}
	
	public void collision() {
		if(x < 0) {
			velX = .13;
		}else if(x > Game.WIDTH - main.getWidth()){
			velX = -.13;
		}
	}
	
	public Rectangle getBounds() {
		return new Rectangle((int)x, (int)y, main.getWidth(), main.getHeight());
	}
	
}

Entire Zombie Class:


package com.pickens.objects;

import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Rectangle;

public class Zombie {

	float x, y;
	
	Player p;
	
	int[] duration = {200, 200};
	
	Animation lt;
	Animation rt;
	
	boolean left = false, right = false;
	
	Animation main;
	
	Rectangle bounds;
	
	public Zombie(float x, float y, Player p) throws SlickException {
		this.x = x;
		this.y = y;
		this.p = p;
		
		init();
	}
	
	public void init() throws SlickException {
		Image[] lt = {new Image("res/Zombie_Left1.png"), new Image("res/Zombie_Left2.png")};
		Image[] rt = {new Image("res/Zombie_Right1.png"), new Image("res/Zombie_Right2.png")};
		
		this.lt = new Animation(lt, duration, false);
		this.rt = new Animation(rt, duration, false);
		
		if(x < p.x) {
			main = this.rt;
			right = true;
		}else{
			main = this.lt;
			left = true;
		}
		
		
	}
	
	public void render(Graphics g) {
		main.draw((int) x, (int) y);
		g.setColor(Color.red);
		g.draw(getBounds());
	}
	
	public void update(int delta) {
		
		collision();
		
		main.update(delta);
		
		if(right) {
			x += .13;
		}else{
			x -= .13;
		}
	}
	
	public void collision() {
		if(getBounds().intersects(p.getBounds())) {
			System.exit(0);
		}
	}
	
	public Rectangle getBounds() {
		return new Rectangle((int)x, (int)y, main.getWidth(), main.getHeight());
	}
	
}


Along with the entire ZombieController class:


package com.pickens.objects.controllers;

import java.util.ArrayList;
import java.util.Random;

import javax.swing.Timer;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;

import com.pickens.objects.Player;
import com.pickens.objects.Zombie;

public class ZombieController {

	ArrayList<Zombie> zombies = new ArrayList<Zombie>();
	
	Player p;
	
	Zombie TempZombie;
	
	float number = 0;
	
	long last = System.currentTimeMillis();
	
	public ZombieController(Player p) {
		this.p = p;
	}
	
	public void render(Graphics g) {
		for(int i = 0; i < zombies.size(); i++) {
			TempZombie = zombies.get(i);
			
			TempZombie.render(g);
		}
	}
	
	public void update(int delta) throws SlickException {
		number += .1;
		if(number >= 200) {
			spawn();
			System.out.println("Zombie Spawn Possbile");
			number = 0;
		}
		for(int i = 0; i < zombies.size(); i++) {
			TempZombie = zombies.get(i);
			
			TempZombie.update(delta);
		}
	}
	
	public void spawn() throws SlickException {
		Random r = new Random();
		if(r.nextInt(99) < 29) { //30% Chance left
			addZombie(new Zombie(640+64,256-128,p));
		}
		if(r.nextInt(99) < 29) { //30% Chance right
			addZombie(new Zombie(-64,256-128,p));
		}
	}
	
	public void addZombie(Zombie e) {
		zombies.add(e);
	}
	
	public void removeZombie(Zombie e) {
		zombies.remove(e);
	}
	
}

Just a quick note, although this is unrelated, are all your images enlarged/upscaled images? (IE: You took a 16x16 image and enlarged it to 64x64? So the actual image file is 64x64?) Because I don’t see a g.scale() anywhere in your render method and you’re getting your window heights/widths via main.getWidth() and main.getHeight(). When you use getWidth and getHeight in slick, it will return the unscaled Width and Height, not scaled. So when you scale, you have to divide the getWidth and getHeight by how much you scaled it. Thus, I suspect you’re not scaling anything. You may be loading images a lot larger than they need to be to get the desired effect you want.

For example, if you use:
g.scale(3,3); // 300% zoom
then… if you need the actual height and width you would use:
gc.getHeight()/3, gc.getWidth()/3 //A third of the height/width.

If you didn’t know, if you put g.scale(2,2); (for 200% zoom, for example) at the top of your render method it will scale the entire game, so you don’t have to make “enlarged” versions of smaller images. Keep in mind if you do this though, everything will be blurred and you’ll have to apply a nearest neightbor filter to all your images:
ExampleImage = new Image(“res/somepic.png”, false, Image.FILTER_NEAREST);

EDIT: Also, you dont have to apply g.scale to everything, if you want you can apply it only to certain objects:
//Stuff you DONT want scaled
g.scale(2,2); //for 200% scaling
//Stuff you DO want scaled
g.scale(1,1);
// More stuff you dont want scaled.

Ya honestly I have no idea what is going on With this glitch… I think I will swap the collision detection two the player to c if the zombie update is called unreliably, I hope I can fix this… I rlly don’t wanna go boilerplate again

Maybe you’re doing other stuff with it elsewhere, but this stands out to me:


//from Zombie.java:
if(x < p.x) {
      main = this.rt;
      right = true;
}else{
      main = this.lt;
      left = true;
}

//and

if(right) {
      x += .13;
}else{
      x -= .13;
}

Why is there a left? Also, is right ever set to false instead of left being set true? Because left is never tested, only right is, and I don’t see it ever set to false. (Other than being false from it’s declaration)

Where is ZombieController.update() called, and how often? (Presumably every frame)

Yeah, I would have a isZombieTouching() or something in the player’s update method, makes more sense than for every zomb to be doing it, at least to me.

Honestly I have no idea about the booleans :stuck_out_tongue: the zombie controller is called in the play state every time the play state update is called… Idk what the problem is ugh

The problem was exactly as what BurntPizza said. The Zombie update as not being called every frame, therefore lagging behind the ‘actual’ collision. After I moved collision handling between the two entities (Player and Zombie) the collision was responsive no matter what was happening. It must’ve been a weird slick thing. (Or a weird I’m a terrible programmer thing :P) Anyways, thanks again BurntPizza along with Ray and all the other helpers. If I remember I will put a few of the most prominent helpers of this post in the credits of my game under special thanks. (If you want)

Congratulations for finding the error. Also, you are creating a new Rectangle every time the getBounds method is called. It will be better to keep it as a private member of the class and update the rectangle every frame instead of creating a new one.

Exactly what I have done. No it seems my next error, A quite annoying one, is sometimes zombies will spawn and not be responsive to collisions.

If you haven’t considered using AABB, I strongly suggest that you do.

When I do collisions, I base it on a range depending on how the object is moving and where it is going. This is an example of how I handle collisions for the bricks game.


//All object collisions are done if the reference points are directly in the center of each object.

if(ballTopCollision[i] && 
   ballposy[i]+ballSize/2 > raftposy && ballposy[i]+ballSize/2 < raftposy+10 && 
   ballposx[i]+ballSize/2 > raftposx-raftSize/2 && ballposx[i]-ballSize/2 < raftposx+raftSize/2){
         //Switch the Ball direction downwards...
         ballTopCollision[i] = false;
         //If the ball is moving upwards and it collides with the raft. Do stuff...
         
}
else if(!ballTopCollision[i] && 
          ballposy[i]-ballSize/2 > raftposy && ballposy[i]-ballSize/2 < raftposy+10 && 
          ballposx[i] > raftposx-raftSize/2 && ballposx[i] < raftposx+raftSize/2){
         //Switch the Ball direction upwards...
         ballTopCollision[i] = true;
         //If the ball is moving downwards and it collides with the raft. Do stuff...
}
                
if(ballLeftCollision[i] && 
   ballposx[i]+ballSize/2 > raftposx-raftSize/2 && ballposx[i]+ballSize/2 < raftposx+raftSize/2 && 
   ballposy[i] > raftposy && ballposy[i] < raftposy+10){
         //Switch the ball direction right
         ballLeftCollision[i] = false;
         //If the ball is moving left and it collides with the raft. Do stuff...
}
else if(!ballLeftCollision[i] && 
          ballposx[i]-ballSize/2 > raftposx-raftSize/2 && ballposx[i]-ballSize/2 < raftposx+raftSize/2 && 
          ballposy[i] > raftposy && ballposy[i] < raftposy+10){
         //Switch the ball direction left
         ballLeftCollision[i] = true;
         //If the ball is moving right and it collides with the raft. Do stuff...
}

If you look closely at the example, you can see that I’m using the entire rectangle to look for a collision. Depending on the speed of the objects, chances are that objects will clip through one another. This helps collisions happen even if you are having clipping issues. Depending on how fast objects are moving toward each other, you can prevent most issues in where an object will fully clip through another object.

Anyway, combined with what you are doing with not creating a new bounding box for each collision, and you’ll have a system in where the robots will have a much wider collision base for reacting to collisions. Research AABB a bit more and it’ll surely fix all your collision problems. If you still can’t solve it, then I’ll probably have to think about giving you a better example.