java: Enemy player tracking AI for java game - java

I am creating a game for the first time in java and I'm currently trying to get my zombies to track and follow the player. below is my code. I have created a controller class, and used a linked list to create multiple zombies. Then in my zombies update method I use simple if statements for the zombie to track the player. The problem is that the zombies track to the players initial starting position, but not to where he is moved.
In the zombie update() method I used a System.println(player.getX()); and it showed that the players position was not being updated in the zombies class, so that is why they only track to its starting position. I cant figure out how to fix this. any help would be appreciated.
ZOMBIE CLASS:
public class Zombie extends Entity{
Animation dwn,up, left, right;
BufferedImage north, south, east, west;
Sprites sprites = new Sprites();
Player player = new Player(100,100);
Rectangle boundsZombie;
String direction = "";
public Zombie(int x, int y){
super(x,y);
sprites.create();
boundsZombie = new Rectangle(0,0, 32, 32);
}
public void update(){
if(player.getX() > x){
x = x + 1;
}
if(player.getX() < x){
x = x - 1;
}
if(player.getY() > y){
y = y + 1;
}
if(player.getY() < y){
y = y - 1;
}
System.out.println(player.getX());
}
public Rectangle getBounds(){
return new Rectangle((int) x, (int) y, 32, 32);
}
public void render(Graphics g){
g.drawImage(sprites.zombieNorth, x, y, null);
//g.setColor(Color.red);
//g.fillRect((int) x + boundsZombie.x, (int) y + boundsZombie.y, boundsZombie.width, boundsZombie.height);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
PLAYER CLASS
public class Player extends Entity{
Animation dwn,up, left, right;
BufferedImage north, south, east, west;
Sprites sprites = new Sprites();
KeyManager keyManager = new KeyManager();
Rectangle boundsPlayer;
//Controller c;
boolean movUp, movDwn, movRight, movLeft;
String direction = "";
public Player(int x, int y){
super(x,y);
sprites.create();
dwn = new Animation(100, sprites.player_down);
up = new Animation(100, sprites.player_up);
left = new Animation(100, sprites.player_left);
right = new Animation(100, sprites.player_right);
north = sprites.playerNorth;
south = sprites.playerSouth;
east = sprites.playerEast;
west = sprites.playerWest;
boundsPlayer = new Rectangle(0,0, 32, 32);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public Rectangle getBounds(){
return new Rectangle((int) x, (int) y, 32, 32);
}
/**
* player tick method, used to move the player
*/
public void update(){
if(Game.getKeyManager().up){
y -= 3;
direction = "north";
}
if(Game.getKeyManager().down){
y += 3;
direction = "south";
}
if(Game.getKeyManager().left){
x -= 3;
direction = "west";
}
if(Game.getKeyManager().right){
x += 3;
direction = "east";
//System.out.println(x);
}
}
public int getX() {
return (int)x;
}
public int getY() {
return (int)y;
}
public String getDirection(){
return direction;
}
/**
* method used to return players facing direction sprite
* #return
*/
public BufferedImage getPlayerDirection(){
if(direction == "north"){
return north;
}
else if(direction == "south"){
return south;
}
else if(direction == "east"){
return east;
}
else{
return west;
}
}
public void render(Graphics g){
g.drawImage(getCurrentAnimationFrame(), (int) x, (int) y, null);
//g.setColor(Color.red);
//g.fillRect((int) x + boundsPlayer.x, (int) y + boundsPlayer.y, boundsPlayer.width, boundsPlayer.height);
}
/**
* method used to return the bufferedImage of current frame
* #return
*/
public BufferedImage getCurrentAnimationFrame(){
if(Game.getKeyManager().right == true){
return right.getCurrentFrame();
}
else if(Game.getKeyManager().left == true){
return left.getCurrentFrame();
}
else if(Game.getKeyManager().up == true){
return up.getCurrentFrame();
}
else if(Game.getKeyManager().down == true){
return dwn.getCurrentFrame();
}
else {
return getPlayerDirection();
}
}
}

This happens because your enemy class has one player object and your game class has a completely different player object. So your player position will always be the same in the enemy class. Hence, it would be more logical to do enemy AI movement in the game class. Your player or enemy class should just hold your character's position, direction or loading a image whether it is a enemy or a player.
I suggest creating a parent class for both enemy and player class because they both have similar characteristics.

Related

Random bouncing ball

I found some answers in internet which explain how to make a ball bouncing, but some of them don't helped me (because bounces are not random), and some are very difficult (my knowledge in physics are limited).
Problem : I have a ball which move and bounce, but not randomly, and I want to make this ball bouncing randomly.
I already have this piece of code :
Public Class Ball{
/** Coordinates */
private int x, y;
/** Speed of the ball*/
private int speedX, speedY;
public Ball(int x, int y, int speedX, int speedY) {
this.x = x ;
this.y = y ;
this.speedX = speedX;
this.speedY = speedY;
}
public void moveAndBounce() {
x += speedX ;
y += speedY ;
// Assuming that touchWallHorizontal() and touchWallHorizontal() work
if (touchHorizontalWall()) {
speedX = -speedX ;
}
if (touchVerticalWall()) {
speedY = -speedY ;
}
}
public static void main (String [] args){
Ball ball = new Ball(); // Initialisation
while (true){
ball.moveAndBounce() ;
}
}
}
It works perfectly, but the ball movement is always the same (a rectangle).
Question : is there a way to have a random bouncing without add attributes to the ball (just with coordinates and speed) ?
And if not, is there not too difficult solution to resolve this problem ?
Thanks in advance !
Problem solved. Here is the new code for moveAndBounce() method (Thanks to johnHopkins) :
public void moveAndBounce() {
x += speedX ;
y += speedY ;
Random random = new Random();
if (touchHorizontalWall()) {
if (speedX > 0) {
// New speed between 10 and 15 (you can choose other)
setSpeedX(-random.nextInt(15) + 10);
} else {
setSpeedX(random.nextInt(15) + 10
}
}
if (touchVerticalWall()) {
if (speedY > 0) {
setSpeedY(-random.nextInt(15) + 10);
} else {
setSpeedY(random.nextInt(15) + 10);
}
}
}

Collision Detection with Java, Slick2D and Tiled Map Editor

I've been stuck on collision detection and how to handle it for a VERY long time. I need help understanding how to use collision detection with Tiled map editor. I have the TMX file parsed and displayed with a player, camera and keyboard movement. I'm not sure how to about doing to collision. My map has 2 layers, one for grass and tiles you can walk on, the other is objectLayer. I've seen people saying I should loop through all the tiles in an object layer and assign rectangles to them. I've got NO idea how to do that and I'm looking for some insight.
My main question is: how do I loop through my layer and get all the tiles and assign rectangles to them.
Game Class:
public class Game extends BasicGameState {
Player player;
Camera cam;
Map map = new Map();
public void init(GameContainer container, StateBasedGame sbg) throws SlickException {
map.init();
cam = new Camera(0, 0);
player = new Player(new Image("res/textures/player.png"), container.getWidth() / 2, container.getHeight() / 2, 32, 32);
}
public void update(GameContainer container, StateBasedGame sbg, int delta) throws SlickException {
cam.tick(player);
player.update(container, delta, map);
}
public void render(GameContainer container, StateBasedGame sbg, Graphics g) throws SlickException {
g.translate(-cam.getX(), -cam.getY());
map.render();
player.render(g);
g.translate(cam.getX(), cam.getY());
}
public int getID() {
return 1;
}
}
Player Class:
public class Player {
float x, y;
int width, height;
double velX = 0.4;
double velY = 0.4;
Image img;
public Player(Image img, float x, float y, int width, int height) {
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void init() throws SlickException {
}
public void update(GameContainer container, int delta, Map map) {
Input input = container.getInput();
if (input.isKeyDown(Input.KEY_D)) x += velX;
if (input.isKeyDown(Input.KEY_A)) x -= velX;
if (input.isKeyDown(Input.KEY_W)) y -= velY;
if (input.isKeyDown(Input.KEY_S)) y += velY;
}
public void render(Graphics g) {
//g.scale(-2, -2);
g.drawImage(img, x, y);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
Map class:
public class Map {
TiledMap map;
public void init() throws SlickException {
map = new TiledMap("res/map/zenith.tmx");
}
public void render() throws SlickException {
map.render(0, 0);
}
}
I'm not too familiar with the methods exactly for getting the tile, but it's definitely in the Javadoc. http://slick.ninjacave.com/javadoc/
For checking collisions against a player, you need to change your movement to check whether the new location of the player is going to have a collision, otherwise you'll get stuck on them.
public void update(GameContainer container, int delta, Map map) {
Input input = container.getInput();
float dx = 0, dy = 0;
// add to dx and dy instead of moving the player straight away.
if (input.isKeyDown(Input.KEY_D)) dx += velX;
if (input.isKeyDown(Input.KEY_A)) dx -= velX;
if (input.isKeyDown(Input.KEY_W)) dy -= velY;
if (input.isKeyDown(Input.KEY_S)) dy += velY;
// here we check the collisions, you can fiddle with this if statement and it'll behave differently.
if(dx != 0 || dy != 0){
if(map.checkCollision(x+dx,y+y){
x+=dx;
y+=dy;
}
}
}
As for checking the collision itself you need a new method in your map class that creates a rectangle for the player and checks against a 2D array inside the map class which holds your collisions. You can populate this array when the map is intialised by looping through your Tiled map through the layer you need and checking if the tile is a collision with getTile(x,y). If the tile has the correct properties for a collision you create a rectangle in that location in your 2D array with something like this:
collisionArray[x][y] = new Rectangle(x,y,TILEWIDTH,TILEHEIGHT);
You can now check for the collision inside
map.checkCollision(x,y)
and with a rectangle created for the player using
Rectangle player = new Rectangle(x,y,PLAYERWIDTH,PLAYERHEIGHT);
Putting it all together you can then check for an intersection with any tiles in the collision array with .intersect(r) as mentioned earlier and map.collision can return true to the player which should stop movement of the player.
I hope that wasn't too convoluted, the javadoc is very helpful and Kevin Glass wrote a thing about checking collisions with just 2D arrays, with varying collision box sizes.
http://www.cokeandcode.com/main/tutorials/tile-maps/
Ok, now im getting a null pointer exception in my checkCollision()
public void assignRectangles() {
int objectLayer = map.getLayerIndex("objectLayer");
for (int x = 0; x < map.getWidth(); x++) {
for (int y = 0; y < map.getHeight(); y++) {
if (map.getTileId(x, y, objectLayer) == 1){
collisionArray[x][y] = new Rectangle(x * 32, y * 32, 32, 32);
}
}
}
}
public boolean checkCollision(float x, float y) {
for (int j = 0; j < collisionArray.length; j++) {
for (int i = 0; i < collisionArray[j].length; i++) {
if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y) {
return false;
}
}
}
return true;
}
Im getting it on the line where it say if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y)
Not sure why im getting it though

Libgdx Actor undetected touch input

I am looking for touch detection. The code showed below is what I did to set a circle in my app. I want detect touch on this circle and not around or on the entire texture. The strange thing is that touch isn't detected, in nowhere I can detect it
Circle class:
public class Circle_Obj extends Actor{
private Vector2 position;
private float radius;
private com.badlogic.gdx.math.Circle circle;
private Texture texture;
public Circle_Obj(float x, float y, float radius) {
position = new Vector2(x,y);
this.radius = radius;
circle = new com.badlogic.gdx.math.Circle(x,y,radius);
texture = new Texture(Gdx.files.internal("texture.png"));
addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("TOUCHED", " TOUCHED ");
return true;
}
});
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture,0, 0);
}
}
Screen class :
public class GameScreen implements Screen {
private Stage stage;
private Circle_Obj circle_obj;
public GameScreen() {
circle_obj = new Circle_Obj(Static_values.Width/2, Static_values.Height/2, Static_values.Width / 100 * 10);
stage = new Stage(new FitViewport(Static_values.Width/3, Static_values.Height/3));
stage.addActor(circle_obj);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.draw();
}
#Override
public void dispose() {
stage.dispose();
}
/** other methods **/
}
You can use libgdx's touch detection in a circle class.Only touched circle will affected.
public boolean is_touched() {
if (Gdx.input.justTouched()) {
float xx = Gdx.input.getX();
float yy = Gdx.input.getY();
float x = position.x;
float y = position.y;
return (xx - x) * (xx - x) + (yy - y) * (yy - y) < radius * radius;
}
}
If you are using a lot of circles so it is better for performance to take touch position as a parameter.
in the circle class
public boolean is_touched(float xx,float yy) {
float x = position.x;
float y = position.y;
return (xx - x) * (xx - x) + (yy - y) * (yy - y) < radius * radius;
}
}
and in another class
if (Gdx.input.justTouched()) {
float xx = Gdx.input.getX();
float yy = Gdx.input.getY();
if (circle0.is_touched(xx, yy)) {
// do something about circle0
}
if (circle1.is_touched(xx, yy)) {
// do something about circle1
}
if (circle2.is_touched(xx, yy)) {
// do something about circle2
}
}
You can also ignore one of the touched circles when two circles overlaps and user touchs the overlapping area.
if (Gdx.input.justTouched()) {
float xx = Gdx.input.getX();
float yy = Gdx.input.getY();
if (circle0.is_touched(xx, yy)) {
// do something about circle0
}
else if (circle1.is_touched(xx, yy)) {
// do something about circle1
}
else if (circle2.is_touched(xx, yy)) {
// do something about circle2
}
}
To detect the touch on an actor added in a Stage, the method hit of the actor is called (hit detection in the documentation)
public Actor hit (float x, float y, boolean touchable) {
if (touchable && getTouchable() != Touchable.enabled) return null;
return x >= 0 && x < width && y >= 0 && y < height ? this : null;
}
You have not set a size to your Circle_Obj actor, so hit will always return null.
Now, your actor is a circle, so you probably want to override hit so that it checks if the given coordinates ares in the circle instead of the default implementation that checks if the coordinates are in a box of the size of the actor.
Something like:
#Override
public Actor hit (float x, float y, boolean touchable) {
if (touchable && getTouchable() != Touchable.enabled) return null;
return (x - position.x)*(x- position.x) + (y - position.y)*(y - position.y) < radius*radius ? this : null;
}
If you inherit from Actor, you need to set the bounds or it will not be click/touch-able!.
Simply set the bounds to match the texture your Actor contains.
//add this Set bounds the x, y, width, and height
circle_obj.setBounds(0, 0,texture.getWidth(), texture.getHeight());

Static count(Android)

In the method bounce() the value of count in first increment is not taken forward to second increment..I meant supose count=0..then first time i increment count its value becomes 1 nd second time also the vaue becomes 1..thats may be because both count are stored in different memory location...if i make count static then each time i run the application it retains the value stored in it last time which i don't want..i want the value of count in first increment should be taken forward to second increment...also i can't put count outside if loop inside bounce() method as it will give the wroung output..what should i do?
public class Ball {
private Point p; //Represents the x and y position of the Ball
private int c; //Represents the color of the Ball
private int r; //Represents the Radius of the Ball.
private int dx; //Represents the change in x position of the Ball. (Horizontal Speed)
private int dy; //Represents the change in y position of the Ball. (Vertical Speed)
private Paint paint; //Android Object holding the color for drawing on the canvas
public int count = 0;
public Ball(int x,int y,int col,int radius)
{
p=new Point(x,y);
c=col;
r=radius;
paint=new Paint();
dx=0;
dy=0;
}
public int getCount()
{return count;
}
public int getX() //Returns the x position of the ball as an integer
{return p.x;
}
public int getY() //Returns the y position of the ball as an integer
{
return p.y;
}
public int getRadius() //Returns the Radius of the ball as an integer
{return r;
}
public Paint getPaint() //Returns the Paint of the Ball as a Paint Object
{return paint;
}
public void setColor(int col) //Sets the Color of the Ball
{c=col;
if (paint == null)
paint = new Paint();
paint.setColor(col);
}
public void goTo(int x,int y) //Sets the x and y positions of the Ball
{p.x=x;
p.y=y;
}
public void setDX(int speed) //Sets the Horizontal Speed of the Ball
{dx=speed;
}
public void setDY(int speed) //Sets the Vertical Speed of the Ball
{
dy=speed;
}
public void move() //Changes the x and y position by the dx and dy values
{
p.x=p.x+dx;
p.y=p.y+dy;
}
public void setX(int x)
{
p.x = x;
}
public void setY(int y)
{
p.y = y;
}
public int getDX()
{
return p.x;
}
public int getDY()
{
return p.y;
}
public void bounce(Canvas canvas) //Bounces off the Sides of the Canvas
{
move();
if(p.x>canvas.getWidth()|| p.x<0)
{
count++;
dx=dx * -1;
}
if(p.y>canvas.getWidth()|| p.y<0)
{
count++;
dy=dy * -1;
}
MainActivity.tv.setText(String.valueOf(getCount()));
}
}
Just thought I should add my comments as an answer. You should only create one instance of the Ball class and use it. Each time you create a new instance of Ball, you set count = 0
You should use singleton class to maintain your count increment ,and whenever application starts make it zero . instead of static making Singleton class and maintain count in that class using setter and getter method is beat easy .

Java - How to give each tile generated from an array a unique "ID"

I am trying to do mouse picking and the tile I click on changes to whatever the opposite tile is ie. grass to dirt, but every grass tile has the same "ID" so every grass tile on the screen changes to dirt. How can I go about generating these tiles in a better way? I want it to be randomly generated and not drawn from an array map of like 000001100.
Block class
public class Block {
public enum BlockType {
Dirt,
Grass,
Selection
}
BlockType Type;
Vector2f Position;
Image texture;
boolean breakable;
public Block(BlockType Type, Vector2f Position, Image texture, boolean breakable) {
this.Type = Type;
this.Position = Position;
this.texture = texture;
this.breakable = breakable;
}
public BlockType getType() {
return Type;
}
public void setType(BlockType value) {
Type = value;
}
public Vector2f getPosition() {
return Position;
}
public void setPosition(Vector2f value) {
Position = value;
}
public Image gettexture() {
return texture;
}
public void settexture(Image value) {
texture = value;
}
public boolean getbreakable() {
return breakable;
}
public void setbreakable(boolean value) {
breakable = value;
}
}
Tile Generation Class
public class TileGen {
Block block;
public Block[] tiles = new Block[2];
public int width, height;
public int[][] index;
boolean selected;
int mouseX, mouseY;
int tileX, tileY;
Image dirt, grass, selection;
SpriteSheet tileSheet;
public void init() throws SlickException {
tileSheet = new SpriteSheet("assets/tiles/tileSheet.png", 64, 64);
grass = tileSheet.getSprite(0,0);
dirt = tileSheet.getSprite(1,0);
selection = tileSheet.getSprite(2,0);
tiles[0] = new Block(BlockType.Grass, new Vector2f(tileX,tileY), grass, true);
tiles[1] = new Block(BlockType.Dirt, new Vector2f(tileX,tileY), dirt, true);
width = 50;
height = 50;
index = new int[width][height];
Random rand = new Random();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
index[x][y] = rand.nextInt(2);
}
}
}
public void update(GameContainer gc) {
Input input = gc.getInput();
mouseX = input.getMouseX();
mouseY = input.getMouseY();
tileX = mouseX / width;
tileY = mouseY / height;
if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
selected = true;
}
else{
selected = false;
}
System.out.println(tiles[index[tileX][tileY]]);
}
public void render() {
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
tiles[index[x][y]].texture.draw(x * 64, y *64);
if(IsMouseInsideTile(x, y))
selection.draw(x * 64, y * 64);
if(selected && tiles[index[x][y]].breakable) {
if(tiles[index[tileX][tileY]].Type == BlockType.Grass)
tiles[index[tileX][tileY]].texture = dirt;
}
}
}
}
public boolean IsMouseInsideTile(int x, int y)
{
return (mouseX >= x * 64 && mouseX <= (x + 1) * 64 &&
mouseY >= y * 64 && mouseY <= (y + 1) * 64);
}
I am using slick2d library. I'm not sure if ID is the right word, but I hope you can understand what I am trying to ask.
It looks like your existing structure is fine, but it doesn't look like you understand what it does. The int[][] index array holds a grid of tile types, corresponding to x and y coordinates. To change the tile type at a particular coordinate, all you need to do is set the index array to the type you want.
Specifically, in your render function, you would have something like:
if(IsMouseInsideTile(x, y) && selected && tiles[index[x][y]].breakable)
if(tiles[index[tileX][tileY]].Type == BlockType.Grass)
tiles[index[tileX][tileY]].texture = dirt;
I'd try to figure out exactly what the code you have is doing before modifying it further.
Note: why is this in the render function anyways? You should probably have it in its own function or at least inside your update function.

Categories

Resources