I am making a game in Java (No Libraries).
It's a 2D top-down game where the player can walk and is faced towards the mouse cursor.
public Player(int x, int y, int health, int tileId) {
super(x, y, health);
tile = new Tile(tileId, false);
mouseInput = new MouseHandler(screen);
}
public void tick() { // Executed by game tick.
// x = playerX and y = playerY
int cursorX = mouseInput.getMousePos()[0];
int cursorY = mouseInput.getMousePos()[1];
float X = cursorX - x;
float Y = cursorY - y;
rotation = Math.atan2(Y, X);
}
It looks good as long the player is at (0,0)
If the player moves and the mouse coordinates become negative it begins to show strange behaviour (Look at video below)
Youtube: https://www.youtube.com/watch?v=M6ZHCrWvt3Y
The rotation of the sprite is done in another class 'Screen.java'
By using:
if (rotation < 360)
rotation++
else
rotation = 0
I verified that the rotation is working correctly.
EDIT:
public BufferedImage rotate(BufferedImage img, double degree) {
AffineTransform tx = new AffineTransform();
tx.rotate(degree, 4, 4);
AffineTransformOp op = new AffineTransformOp(tx,AffineTransformOp.TYPE_BILINEAR);
BufferedImage image = op.filter(img,null);
return image;
}
Okay i fixed it.
The problem was the game scale i am making an 2d game and set the width, height and the scale.
But i didn't divide the mouseX and mouseY by the scale.
public void mouseMoved(MouseEvent e) {
mouseX = e.getX() / game.getScale();
mouseY = e.getY() / game.getScale();
}
I found the problem by accident when messing with the gamescale.
Related
I am creating a top-down shooter game, and whenever I move the camera, or zoom, black likes appear like a grid
I am using Tiled to create the map, and I have the camera following my centered box2d body. I have found that making the camera position equal the position of the box2d body with an int cast results in the black lines disappearing like this:
The problem though, is that because I have the game scaled down, the player will move for a second or two and then when the player reaches the next whole number on either axis, the camera snaps to the player, which is not what I want for the game as it's jarring. The player's movement is granular, but, while rounded, the camera's is not. I do not know if this is a problem with my tile sheet or if it's something I can fix by altering some code. I have tried all different kinds of combinations of padding, and values of spacing and margins. So ultimately, how can I have the camera match the player's position smoothly and not cause the black lines? I'd greatly appreciate any help or recommendations. Thank you in advance!
Where I am type casting the player's float position to an int in game class:
public void cameraUpdate(float delta) {
//timeStep = 60 times a second, velocity iterations = 6, position iterations = 2
world.step(1/60f, 6, 2); //tells game how many times per second for Box2d to make its calculations
cam.position.x = (int)playerOne.b2body.getPosition().x;
cam.position.y = (int)playerOne.b2body.getPosition().y;
cam.update();
}
Majority of player class:
public class PlayerOne extends Sprite implements Disposable{
public World world; // world player will live in
public Body b2body; //creates body for player
private BodyDef bdef = new BodyDef();
private float speed = 1f;
private boolean running;
TextureAtlas textureAtlas;
Sprite sprite;
TextureRegion textureRegion;
private Sound runningSound;
public PlayerOne(World world) {
this.world = world;
definePlayer();
textureAtlas = new TextureAtlas(Gdx.files.internal("sprites/TDPlayer.atlas"));
textureRegion = textureAtlas.findRegion("TDPlayer");
sprite =new Sprite(new Texture("sprites/TDPlayer.png"));
sprite.setOrigin((sprite.getWidth() / 2) / DunGun.PPM, (float) ((sprite.getHeight() / 2) / DunGun.PPM - .08));
runningSound = Gdx.audio.newSound(Gdx.files.internal("sound effects/running.mp3"));
}
public void definePlayer() {
//define player body
bdef.position.set(750 / DunGun.PPM, 400 / DunGun.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
//create body in the world
b2body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(12 / DunGun.PPM);
fdef.shape = shape;
b2body.createFixture(fdef);
}
public void renderSprite(SpriteBatch batch) {
float posX = b2body.getPosition().x;
float posY = b2body.getPosition().y;
float posX2 = (float) (posX - .14);
float posY2 = (float) (posY - .1);
sprite.setSize(32 / DunGun.PPM, 32 / DunGun.PPM);
sprite.setPosition(posX2, posY2);
float mouseX = Level1.mouse_position.x; //grabs cam.unproject x vector value
float mouseY = Level1.mouse_position.y; //grabs cam.unproject y vector value
float angle = MathUtils.atan2(mouseY - getY(), mouseX - getX()) * MathUtils.radDeg; //find the distance between mouse and player
angle = angle - 90; //makes it a full 360 degrees
if (angle < 0) {
angle += 360 ;
}
float angle2 = MathUtils.atan2(mouseY - getY(), mouseX - getX()); //get distance between mouse and player in radians
b2body.setTransform(b2body.getPosition().x, b2body.getPosition().y, angle2); //sets the position of the body to the position of the body and implements rotation
sprite.setRotation(angle); //rotates sprite
sprite.draw(batch); //draws sprite
}
public void handleInput(float delta) {
setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2 + (5 / DunGun.PPM));
this.b2body.setLinearVelocity(0, 0);
if(Gdx.input.isKeyPressed(Input.Keys.W)){
this.b2body.setLinearVelocity(0f, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.S)){
this.b2body.setLinearVelocity(0f, -speed);
}if(Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, speed);
}
if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, -speed );
}if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, -speed);
}
Where I declare the pixels per meter scale:
public class DunGun extends Game{
public SpriteBatch batch;
//Virtual Screen size and Box2D Scale(Pixels Per Meter)
public static final int V_WIDTH = 1500;
public static final int V_HEIGHT = 800;
public static final float PPM = 100; //Pixels Per Meter
Game render and resize methods:
#Override
public void render(float delta) {
cameraUpdate(delta);
playerOne.handleInput(delta);
//clears screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
cam.zoom -= .01;
}
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
cam.zoom += .01;
}
mapRenderer.render();
b2dr.render(world, cam.combined); //renders the Box2d world
mapRenderer.setView(cam);
//render our game map
//mapRenderer.render(); // renders map
//mapRenderer.render(layerBackround); //renders layer in Tiled that p1 covers
game.batch.setProjectionMatrix(cam.combined); //keeps player sprite from doing weird out of sync movement
mouse_position.set(Gdx.input.getX(), Gdx.input.getY(), 0);
cam.unproject(mouse_position); //gets mouse coordinates within viewport
game.batch.begin(); //starts sprite spriteBatch
playerOne.renderSprite(game.batch);
game.batch.end(); //starts sprite spriteBatch
//mapRenderer.render(layerAfterBackground); //renders layer of Tiled that hides p1
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true); //updates the viewport camera
}
I solved it by fiddling around with the padding of the tilesets in GDX Texture Packer. I added 5 pixels of padding around the 32x32 tiles. I set the margins to 2, and spacing to 4 in Tiled. I had tried a lot of different combinations of padding/spacing/margins that didn't work which made me think it was a coding problem, but those settings worked, and I didn't have to round the floats.
I think that's a simple error, but I've crashed my mind thinking about it and I can't do it.
I don't know why when I jump with the player and collides with another rectangle (platform) the player appears above it.
for(RectangleMapObject rectangleObject : map.getLayers().get("platform").getObjects().getByType(RectangleMapObject.class)){
Rectangle rectangle = rectangleObject.getRectangle();
float x = rectangle.x;
float y = rectangle.y;
float width = rectangle.width;
float height = rectangle.height;
shapeRenderer.rect(x, y, width, height);
//TODO: Finish tile's physics
if(Intersector.overlaps(rectangle, nihanRect)) {
if (velocity.y > 0) {
nihan.getVelocity().y = 0;
nihan.getPosition().y = y - height - Constants.NIHAN_HEIGHT - 10;
nihan.setCollisions(false);
nihan.setAvailableJump(false);
} else {
nihan.getPosition().y = y + height - 0.5f;
nihan.setCollisions(true);
nihan.setAvailableJump(true);
if(Gdx.input.isKeyPressed(Input.Keys.Z)){
nihan.getPosition().y = y + height;
}
}
nihan.getVelocity().y = 0;
Gdx.app.log(TAG, "Collision produced!");
}else{
nihan.setCollisions(false);
}}
Sorry if my english is bad, I'm a spanish speaker.
Greetings!
When the player is standing -->
After jumping the player appears in the top of the platform
I changed variable "velocity.y" to "nihan.getVelocity.y"
when shooting (slow) bullets in my Java game, they move at incorrect angles, however when sped up they become more and more accurate.
My x, y, speed and directions are all int, however I've tried converting to floats for more accuracy but I'm still having the same error. I believe it's happening because the lowest movement steps I can have are in integers like (+2x and +1y a step and not +1.7x and +0.88y - and I can't be on 0.5 of a pixel)
How do I 'microstep' the bullets to shoot them on the correct angle?
The only other solution I can think of to shoot them at the correct angle is to calculate the end collision point and step towards that point.
Desired behavior is for bullets to shoot at the correct angle (player to mouse) rather then at 'off' angles based on the bullets speed.
public class Bullet extends GameObject
{
private int x;
private int y;
private int speed = 2;
private int direction;
private int length = 70;
public Bullet(int x, int y, int direction)
{
this.x = x;
this.y = y;
this.direction = direction; //Set the direction.
}
public void update(Game game, GameController gc, float dt)
{
x += GameController.lengthdir_x(speed, direction);
y += GameController.lengthdir_y(speed, direction);
}
public void render(Game game, Renderer r)
{
//Draw the bullet with the tail behind it.
r.drawLine(x, y, x + GameController.lengthdir_x(length, direction - 180), y + GameController.lengthdir_y(length, direction - 180), color);
r.drawText("Dir: " + direction, x + 50, y + 20, 0xff0077ff); //Draws the players angle.
}
}
Lengthdir Code: (The angle calculates correctly as I can draw a line between two points perfectly, just when I add movement it messes up)
public static int lengthdir_x(int len, int dir)
{
return (int) (len * Math.cos(Math.toRadians(dir - 90)));
}
public static int lengthdir_y(int len, int dir)
{
return (int) (len * Math.sin(Math.toRadians(dir - 90)));
}
I've also tried doubles for variables: https://pastebin.com/fbrF17bD
Example: http://puu.sh/x9OnN/be4e3f2c80.png
The long blue line is from the player to the mouse, the yellow lines are bullets which are at the correct angle it was shot at - but not travelling the correct direction which should be exactly on the blue line. This was at a bullet speed of 2 - if the bullets are at a speed of 20, they are much closer to the blue line as per the next img: http://puu.sh/x9OwY/a54f201c91.png
I got your Problem: you use Integer-cast on the calculation result, that means you just remove everything after ., so if you get 1.9 as result you will return 1 as length. If you increase speed this error will be reduced, thats why you get better result for higher speed. You need to round your result before you return it. On the other hand you should really change to double. In the code you shown where you use double you didn't changed it in length-function, thats why you don't get better result using double. So your code should look like this:
public static double lengthdir_x(int len, int dir)
{
//don't cast here to int!!!!
return len * Math.cos(Math.toRadians(dir - 90));
}
public class Bullet extends GameObject
{
private double x;
private double y;
private int speed = 2;
private int direction;
private int length = 70;
public Bullet(double x, double y, int direction)
{
this.x = x;
this.y = y;
this.direction = direction; //Set the direction.
}
public void update(Game game, GameController gc, float dt)
{
x += GameController.lengthdir_x(speed, direction);
y += GameController.lengthdir_y(speed, direction);
}
public void render(Game game, Renderer r)
{
//Draw the bullet with the tail behind it.
r.drawLine((int)Math.round(x), (int)Math.round(y), x + GameController.lengthdir_x(length, direction - 180), y + GameController.lengthdir_y(length, direction - 180), color);
r.drawText("Dir: " + direction, (int)x + 50, (int)y + 20, 0xff0077ff); //Draws the players angle.
}
}
Maybe you will need to convert something to int or double somewhere, but make sure lengthdir returns double as result or at least (int)Math.round(...)
I have created a simple rendition of the classic Pong game on Android Studio for a college course. At this point, almost everything is ready to go, except for the fact that I created an unbeatable AI for the enemy paddle. I created the paddles and the ball with the default RectF class and change my AI paddle's position based on the ball's current position (I subtract/add by 65 because my paddles' lengths are 130 pixels and it centers the paddle compared to the ball). This essentially allows the enemy paddle to move at an infinite speed because it is matching the ball's speed/position (the ball speed increases with each new level). Here is that method:
public void AI(Paddle paddle) {
RectF paddleRect = paddle.getRect();
RectF ballRect = ball.getRect();
if (ballRect.left >= 65 && ballRect.right <= screenX - 65) {
paddleRect.left = (ballRect.left - 65);
paddleRect.right = (ballRect.right + 65);
}
if (ballRect.left < 65) {
paddleRect.left = 0;
paddleRect.right = 130;
}
if (ballRect.right > screenX - 65) {
paddleRect.left = screenX - 130;
paddleRect.right = screenX;
}
}
For reference, here are the relevant parts of my Paddle class:
public Paddle(float screenX, float screenY, float xPos, float yPos, int defaultSpeed){
length = 130;
height = 30;
paddleSpeed = defaultSpeed;
// Start paddle in the center of the screen
x = (screenX / 2) - 65;
xBound = screenX;
// Create the rectangle
rect = new RectF(x, yPos, x + length, yPos + height);
}
// Set the movement of the paddle if it is going left, right or nowhere
public void setMovementState(int state) {
paddleMoving = state;
}
// Updates paddles' position
public void update(long fps){
if(x > 0 && paddleMoving == LEFT){
x = x - (paddleSpeed / fps);
}
if(x < (xBound - 130) && paddleMoving == RIGHT){
x = x + (paddleSpeed / fps);
}
rect.left = x;
rect.right = x + length;
}
The fps in the update(long fps) method above is passed through from the run() method in my View class:
public void run() {
while (playing) {
// Capture the current time in milliseconds
startTime = System.currentTimeMillis();
// Update & draw the frame
if (!paused) {
onUpdate();
}
draw();
// Calculate the fps this frame
thisTime = System.currentTimeMillis() - startTime;
if (thisTime >= 1) {
fps = 1000 / thisTime;
}
}
}
My paddles and ball constantly update in my onUpdate() method in the View class.
public void onUpdate() {
// Update objects' positions
paddle1.update(fps);
ball.update(fps);
AI(paddle2);
}
How can I attach a speed limit to my AI paddle using the paddle speed I already define for each new paddle? I've already tried modifying the AI(Paddle paddle) method to incorporate +/- (paddleSpeed / fps) and it didn't affect the paddle seemingly at all. Maybe I just implemented it wrong, but any help would be fantastic!
To make the AI move the paddle at a steady rate rather than jumping to the correct spot, just try to make the middle of the paddle line up with the middle of the ball:
public void AI(Paddle paddle) {
RectF paddleRect = paddle.getRect();
RectF ballRect = ball.getRect();
float ballCenter = (ballRect.left + ballRect.right) / 2;
float paddleCenter = (paddleRect.left + paddleRect.right) / 2;
if (ballCenter < paddleCenter) {
setMovementState(LEFT);
}
else {
setMovementState(RIGHT);
}
}
and then after you call AI(paddle2) inside of onUpdate, call paddle2.update(fps). This way you aren't repeating yourself with the drawing code. You will notice that the AI paddle may not do perfectly if the ball is much faster than the paddle. In this case you can use math to anticipate where the ball will end up and work on getting there.
In order to build a tic-tac-toe game for testing, I have following routine. But problem is that I am getting too many events for just one touch. I suspect isTouched() returns all of down, up, and move. Is there any way to just get up event?
UPDATE: Resolved the issue by employing justTouched() instead.
#Override
public void render() {
// we update the game state so things move.
updateGame();
// First we clear the screen
GL10 gl = Gdx.graphics.getGL10();
gl.glViewport(0, 0, width, height);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Next we update the camera and set the camera matrix
camera.update();
camera.apply(Gdx.gl10);
...
}
private void updateGame() {
// the delta time so we can do frame independant time based movement
float deltaTime = Gdx.graphics.getDeltaTime();
// Has the user touched the screen? then position the paddle
if (Gdx.input.isTouched() && !isProcess) {
// get the touch coordinates and translate them
// to the game coordinate system.
isProcess=true;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
int offx=-width/2;
int offy=-height/2;
float x = Gdx.input.getX();
float y = Gdx.input.getY();
float touchX = 480 * (x
/ (float) width - 0.5f);
float touchY = 320 * (0.5f - y
/ (float) height);
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++)
{
if(touchX >= offx+i*width/3 && touchX < offx+(i+1)*width/3 &&
touchY >= offy+j*height/3 && touchY < offy+(j+1)*height/3)
{
if(isCurrentO)
data[i][j]=CellStatus.O;
else
data[i][j]=CellStatus.X;
isCurrentO=!isCurrentO;
break;
}
}
}
isProcess=false;
}
}
An alternative to using justTouched is to implement the InputProcessor interface, as it has a touchUp(x,y,pointer,button) which gives you greater control over the input. There are several classes that implement this or you can have your class implement it.
You can create a board for example (with hash map) and each object in your game wants to be clickable add itself to that board if an object was touched and was in board it will catch the event. If not it will not catch the event. So easy! :)