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(...)
Related
It's basically a simple game, in that, when the mouse pointer is within the circle, it adds one to the score. I'm new to processing and tried to create this simple game.
Here's the code:
float dist;
float score;
float x;
float y;
float ran;
float a;
float b;
void setup(){
size(800,600);
background(0);
score = 0;
}
void draw(){
background(0);
text("score: "+score,600,20);
x = random(800);
y = random(600);
circle_(x,y);
if ( (abs(mouseX - x) <= 200) && (abs(mouseY - y) <= 200 )) { // algorithm for checking whether mouse inside the circle or not
score = score + 1;
}
}
void circle_(float x,float y){
delay(1000);
circle(x,y,50);
}
There are 2 problems here: first, you're checking 200 pixels where it should be 25 (as in 25 being half the diameter of the circle). But this is the easy one. The real issue is with the delay(1000); line.
That's the real pain.
It means that your program "sleeps" for about 1000/1001 seconds. While the mouse can go around the screen, this one just isn't listening. It's waiting.
You can track time in Processing with the millis() method. It gives you how many milliseconds have passed since you started running the sketch. I ninja coded you a couple lines to keep track of time and teleport the circle every 1 second. Here's the code:
float score;
float x;
float y;
int respawnCircle;
void setup() {
size(800, 600);
background(0);
score = 0;
}
void draw() {
background(0);
text("score: "+score, 600, 20);
if (millis() > respawnCircle) {
changeCircleCoordinates();
}
circle_(x, y);
if ((abs(mouseX - x) <= 25) && (abs(mouseY - y) <= 25 )) { // using 25 as it's half the circle's diameter
score = score + 1;
// if you want the circle to change place right when you get one point uncomment the next line
// changeCircleCoordinates();
}
}
void changeCircleCoordinates() {
respawnCircle = millis() + 1000;
x = random(800);
y = random(600);
}
void circle_(float x, float y) {
//delay(1000); // this is why your collision detection wasn't working: the program is sleeping for about 1000/1001 milliseconds... you have to be very lucky to be on target at just the right time
circle(x, y, 50);
}
Hope it helps. Have fun!
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.
I have been working on creating a hexagonal (flat top) grid for a simulation I am working on. I have attempted to work out the distance between the hexagons, from a specified target hexagon.
The solution I have works for most of the time, apart from every odd column from the target hexagon north of the target is shifted up by 1. I know that sounds confusing but I have attached an image to explain what I mean:
As you guys can see, the bottom half of the grid below the target hexagon and every other column above the target Hexagon is correct. I cannot understand why :S
Here is an explanation of the Axial & Cube Co-ords.
http://www.redblobgames.com/grids/hexagons/#coordinates
Here is the code responsible for converting the Axial Co-ords to Cube Co-ords.
public void setQR(int theQ, int theR){
this.q = theQ;
this.r = theR;
this.x = this.q;
this.z = this.r - (this.q - (this.q&1)) /2;
this.y = -(this.x + this.z);
}
And heres the code for working out distance.
FYI, the Hexagons are created from a CentrePoint (CPx, CPy).
private double distance = 0;
public double workOutDistance(Hexagon hexagon, HexagonFood target){
double targetX = target.getCPX();
double targetY = target.getCPY();
double hexagonX = hexagon.getCPX();
double hexagonY = hexagon.getCPY();
double deltaX = (targetX-hexagonX)*-1;
double deltaY = (targetY-hexagonY)*-1;
double deltaXRadius = (deltaX/(SimField.hexSize)/1.5);
double deltaYApothem = (deltaY/(SimField.hexSize/1.155)/2);
hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);
ArrayList<Integer> coords = new ArrayList<>();
coords.add(
Math.abs(hexagon.getX() - target.getX())
);
coords.add(
Math.abs(hexagon.getZ() - target.getZ())
);
coords.add(
Math.abs(hexagon.getY() - target.getY())
);
System.out.println(coords);
distance = Collections.max(coords);
return distance;
}
Can anyone please tell me why this is happening ? Would be greatly appreciated.
EDIT:
After changing Int to Double as suggested by Tim, I get this.
http://i.stack.imgur.com/javZb.png
**
SOLUTION
**
after experimenting with the answers given, This small tweak solves the problem.
changing this..
public void setQR(int theQ, int theR){
this.q = theQ;
this.r = theR;
this.x = this.q;
this.z = this.r - (this.q - (this.q&1)) /2;
this.y = -(this.x + this.z);
}
to this..
public void setQR(int theQ, int theR){
this.q = theQ;
this.r = theR;
this.x = this.q;
if (this.r>0){
this.z = this.r - (this.q - (this.q&1))/2;
}
else {
this.z = this.r - (this.q + (this.q&1))/2;
}
this.y = -(this.x + this.z);
}
You're casting a double to an int when calling setQR(); are you sure that's doing what you expect? Doubles use floating point math, so the number you'd expect to be 2.0 might actually be 1.999999989, which would then be rounded down to 1 when cast to an int.
I'm also skeptical of the line that reads this.z = this.r - (this.q - (this.q&1)) /2;. You're adding 1 when the number is odd, which seems to be the failure case you're experiencing; I'd make sure that line is doing what you're expecting, too.
If you're not stepping through this with a debugger and examining the values, you're doing it wrong.
You could also take an entirely different approach to this problem. You know the X/Y (cartesian) coordinates of your two hexagons, which means you can get each hexagon's cubic coordinates relative to the origin of your hexagonal space. The distance between the two hexagons is simply the sum of the absolute values of the differences between the two hexagons' X, Y and Z cubic coordinates. (That is, dist = |h2.X - h1.X| + |h2.Y - h1.Y| + |h2.Z - h1.Z|) So rather than trying to compute the vector between the two centerpoints and then convert that into cubic coordinates, you could just compute the distance directly in cubic coordinates (just like you would if these were squares in cartesian coordinates)...
Even if you take this approach, though, I'd strongly recommend that you debug what's going on with your original approach. Even if you end up throwing away the code, the exercise of debugging will probably teach you valuable lessons that you'll be able to apply in the future.
Note to readers: "cubic" coordinates aren't 3-dimensional cartesian coordinates, they're a hexagon-specific coordinate system for which a link was provided by the OP.
The fact that the computation (that is, the conversion from offset- to cube coordinates, and the computation of the distance in cube coordinates) seems to be correct suggests that Tim was right with his assumption about the floating point errors.
You should try to change the line
hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);
from your original code to something like
hexagon.setQR((int)Math.round(deltaXRadius), (int)Math.round(deltaYApothem));
Which could solve the issue in this case.
If not ... or... in any case, here's a small example, basically doing the same as you did, but as a MVCE...
import java.awt.Point;
public class HexagonsTest
{
public static void main(String[] args)
{
// Above and below
test(8,6, 8,5, 1);
test(8,6, 8,7, 1);
// Left
test(8,6, 7,5, 1);
test(8,6, 7,6, 1);
// Right
test(8,6, 9,5, 1);
test(8,6, 9,6, 1);
// The first one that was wrong:
test(8,6, 7,4, 2);
}
private static void test(int x0, int y0, int x1, int y1, int expected)
{
int distance = computeStepsDistance(x0, y0, x1, y1);
System.out.println(
"Distance of (" + x0 + "," + y0 + ") to " +
"(" + x1 + "," + y1 + ") is " + distance +
", expected " + expected);
}
private static int computeStepsDistance(int x0, int y0, int x1, int y1)
{
Point cp0 = convertOffsetToCubeCoordinates(x0, y0, null);
Point cp1 = convertOffsetToCubeCoordinates(x1, y1, null);
int cx0 = cp0.x;
int cy0 = cp0.y;
int cz0 = -cx0-cy0;
int cx1 = cp1.x;
int cy1 = cp1.y;
int cz1 = -cx1-cy1;
int dx = Math.abs(cx0 - cx1);
int dy = Math.abs(cy0 - cy1);
int dz = Math.abs(cz0 - cz1);
return Math.max(dx, Math.max(dy, dz));
}
private static Point convertOffsetToCubeCoordinates(
int ox, int oy, Point p)
{
int cx = ox;
int cz = oy - (ox - (ox&1)) / 2;
int cy = -cx-cz;
if (p == null)
{
p = new Point();
}
p.x = cx;
p.y = cy;
return p;
}
}
Alright, I'm trying to do some simple object moving in the direction of where you touched the screen.
If I touch directly northwest of the object, it'll kind of move into the direction of the touch position. If I touch directly southeast of the object, it will kind of move into the direction of the touch position as well. However, if I touch directly northeast of the object, it'll move into the opposite direction towards the southwest. If I touch directly southwest of the object, it'll also move to the opposite direction towards northeast.
Also, if I touch north of the object, but just a little to the west, it will go straight west with a little to the north. Same with touching west of the object with a little bit to the north, it'll go straight north with a little bit to the west. Same thing for other directions.
Really, all the directions are from somewhat to obviously incorrect. I've been doing some paper calculations as well and I've seemed to be getting some correct angles, but at this point I'm completely stumped.
Does anyone know what the problem may be?
package com.badlogic.androidgames.texasholdem;
import java.util.List;
import android.util.FloatMath;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.framework.Screen;
public class MainMenuScreen extends Screen {
public static float TO_RADIANS = (1 / 180.0f) * (float) Math.PI;
public static float TO_DEGREES = (1 / (float) Math.PI) * 180;
float num_x = 0; // Position of object on X axis
float num_y = 0; // Position of object on Y axis
float angle = 0;
public MainMenuScreen(Game game) {
super(game);
}
public void update(float deltaTime) {
Graphics g = game.getGraphics();
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
game.getInput().getKeyEvents();
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if(event.type == TouchEvent.TOUCH_UP) {
if(inBounds(event, 0, 0, g.getWidth(), g.getHeight()) ) {
// Calculate the angle of the direction between two points
angle = (float) Math.atan2(event.x - num_x, event.y - num_y) * TO_DEGREES;
if (angle < 0)
angle += 360;
// This is just to give me numbers on the Math.atan2 result, angle, to/from X position, and to/from Y position
System.out.println("Pressed! - ATAN: " + Math.atan2(event.x - num_x, event.y - num_y)
+ " - ANGLE:" + angle + " - POS: " + event.x + "tx/"
+ (int)num_x + "fx " + event.y + "ty/" + (int)num_y + "fy");
}
}
}
// Moving object in direction at 1f speed
num_x += (1f * (float) Math.cos(angle * TO_RADIANS));
num_y += (1f * (float) Math.sin(angle * TO_RADIANS));
}
private boolean inBounds(TouchEvent event, int x, int y, int width, int height) {
if(event.x > x && event.x < x + width - 1 &&
event.y > y && event.y < y + height - 1)
return true;
else
return false;
}
public void present(float deltaTime) {
Graphics g = game.getGraphics();
g.drawPixmap(Assets.background, 0, 0);
g.drawPixmap(Assets.backcard, (int)num_x, (int)num_y);
}
public void pause() {
Settings.save(game.getFileIO());
}
public void resume() {
}
public void dispose() {
}
}
if event x> x then x must be positive to move toward event.x
the problem here is that when event.x< x then your moving x must be negative
int dx,dy;
dx = (1f * (float) Math.cos(angle * TO_RADIANS));
dy = (1f * (float) Math.sin(angle * TO_RADIANS));
if(event.x<x){
dx=-dx;}
if(event.y<y){
dy=-dy;}
num_x+=dx;
num_y+=dy;
this way is simpler but less precise....
public void update(){
//(find dif between item x, and touch x)
float xdif=destx-x;
float ydif=desty-y;
if(x<destx){
dx=xdif/8;
}
else if(x>destx){
//we devide both x and y differences by the same number
dx=xdif/8;
}
else if(x==destx){
dx=0;
}
if(y<desty){
dy=ydif/5;
}
else if(y>desty){
dy=ydif/5;
}
else if(y==desty){
dy=0;
}
x+=dx;
y+=dy;
there u go, pathing in a straight line between two points, item.x and touch x.
Firstly, the math - I think the problem is that, for example, tan(135deg) = tan (-45deg) = -1. Therefore, atan has return values ranging between -90deg and 90deg as a resolution to ambiguity (look at its graph here). I think La5t5tarfighter's solution - negating the x movement in some cases - is on the right track, but you need to negate the y component in those cases as well. You could try that, but it would be much simpler if you used libGDX's Vector2 class. This is how I'd do it:
move.set(touchX, touchY); // y should be through flipping or unproject() before this
move.sub(objectPos); // move now points from object to where you touched
move.nor(); // now 1 unit long
move.scl(SPEED*deltaTime); // multiplied by a constant and delta - framerate-independent
objectPos.add(move);
You could even chain it into just one line if you want:
objectPos.add(move.set(x,y).sub(objectPos).nor().scl(SPEED*deltaTime));
Secondly, you're not using a Camera. I'm not completely sure what the default coordinate system is, but I believe the y axis points up which is not the same as the one used for inputs - Input.getY() is given with an y axis pointing down from the top left corner. If you had a Camera, you'd do this:
cam.unproject(someVector.set(Gdx.input.getX(), Gdx.input.getY(), 0));
Lacking that, you might need to flip the y axis:
event.y = Gdx.graphics.getHeight() - event.y;
Still, this could be wrong. Try drawing the object right at the touch position - if I'm right in this, it'll seem mirrored vertically. If it draws correctly where you touch, ignore this part.
I am working on a game (just for my own fun) in Processing (a variant of Java), and have run into a problem. I have a projectile class that is created and managed by a Castle class, and it goes towards an Enemy class (which is a moving target). What I am trying to do (conceptually) is make this projectile find its intended target (Euclidian distance), say its 20 units away, and move 5 units along that line (i.e. 1/4 of the way there). My problem is that I don't know how to extract the x and y components of that vector to update this projectile's position. Here is my projectile class currently:
class Projectile{
private PImage sprite;
private Enemy target;
private int x;
private int y;
private int speed;
public Projectile(PImage s, Enemy t, int startx, int starty, int sp) throws NullPointerException{
if(t == null){
if(debug){
println("Null target given to Projectile(), throwing exception");
}
throw new java.lang.NullPointerException("The target of the projectile is null");
}
sprite = s;
target = t;
x = startx;
y = starty;
speed = sp;
if(debug){
println("Projectile created: " + t + " starting at position: " + startx + " " + starty);
}
}
public void update(){
if(target != null){
int goingToX = target.getCenterX() ;
int goingToY = target.getCenterY();
//find the total distance to the target
float d = dist(this.x, this.y, target.getCenterX(), target.getCenterY());
//divide it by the velocity vector
d /= speed;
//get the dx and dy components of the vector
}else{//target is null, i.e. already destroyed by something else
//destroy this projectile
//if the higher functions were correct, then nothing needs to go here
//this should be deleted as long as it checks for this.hitTarget()
return;
}
}
public void render(){
image(sprite, x, y, 10, 10);
}
//checks if it hit the target, but does a little bit of rounding because the sprite is big
//done this way in the interest of realism
public boolean hitTarget(){
if(target != null){
if(abs(x - target.getCenterX()) <= 5 && abs(y - target.getCenterY()) <= 5 ){
return true;
}else{
return false;
}
}
//this activates if the target is null, which marks this for deletion
return true;
}
}
I've been researching this for hours and realized my approach was unnecessarily complicated when I was looking into converting floats into strings, formatting them to some number of decimal places, then trying to convert that into a fraction which I would then reduce. I feel like this is far easier than I realize, but I am lacking the math background to do it. All necessary changes should only need to be done in Projectile.update(). Thanks!
Assuming that you want your projectile to 'track' the target, then you can use a simple bit of trig to work out the relative speeds in x and y:
//Calculate the differences in position
float diffX = target.getCenterX() - this.x;
float diffY = target.getCenterY() - this.y;
//Calculate the angle
double angle = Math.atan2(diffY, diffX);
//Update the positions
x += Math.cos(angle) * speed;
y += Math.sin(angle) * speed;
This essentially calculates the angle between the projectile and the target, then moves the projectile in that direction, based on the speed given.