This site requires JavaScript, please enable it in your browser!
Greenfoot back
darkmist255
darkmist255 wrote ...

2012/1/17

Move actor towards actor with double precision

1
2
darkmist255 darkmist255

2012/1/17

#
I've got a bit of a conundrum here, and sadly my math (grade 11) isn't enough to help me get a clear idea of what I should fix here. Since angles lose their precision when moving based on pixels (ints) I almost always use a method similar to this:
public void moveDouble()
{
        realXloc = realXloc + xMomentum;
        realYloc = realYloc + yMomentum;
        
        setLocation((int)realXloc, (int)realYloc);
}
This way, realXloc and realYloc keep track of the position (rather than getY() and getX()). That all works fine and dandy, but now here is my next challenge. I want to have the monster fire a shot in the direction of the player. To do this, I just have it add a new Shot() to the world, then let the "addedToWorld" of the shot do the calculating. In the enemy's "addedToWorld", it calls the method "pointAtObject(character)" (character is the variable with the reference to the player). the "angle =" part is the code from here to determine the angle. It normally uses setRotation(expression) instead of "angle =", but I think it's synonymous in this case.
public void pointAtObject(Actor target)
    {
        if(character != null)
        {
            angle = ((int)(180*Math.atan2(target.getY()-getY(),target.getX()-getX())/Math.PI));
            
            //** This is a slightly modified version of the Move() function in the Greenfoor Actor API. All credit to them.
            double radians = Math.toRadians(angle);

            xMove = (Math.cos(radians));
            yMove = (Math.sin(radians));
        }
    }
now that I have the xMove and yMove variables, I've changed the moveDouble() method
    public void moveDouble()
    {
        realXloc = realXloc + (xMove * xMomentum); // xMomentum and yMomentum are both 4 for test
        realYloc = realYloc + (yMove * yMomentum);
        
        setLocation((int)realXloc, (int)realYloc);
    }
But, as lovely as it can be, the shot moves in a straight line right or left. When I tried "setRotation((int)(180*Math.atan2(target.getY()-getY(),target.getX()-getX())/Math.PI));" all was wonderful, only I lost the precision. Is there a way I can modify something to get that added "virtual pixel" precision? Also, I don't want the object rotating then moving based on rotation, I want to keep the object aligned but move at the specified angle. I'm reading the API move() method right now.
Builderboy2005 Builderboy2005

2012/1/17

#
Where are xMomentum and yMomentum set?
kiarocks kiarocks

2012/1/18

#
If you look they are "4 for the test"
Builderboy2005 Builderboy2005

2012/1/18

#
Ah I see now. Did you print out both xMove and yMove to see what values they were? Do these values make sense? Does the variable radians end up having a value that you would expect given the input? Also why do you take the radian output of atan2(), convert it to degrees, and then re-convert it back into radians again before using sin() and cos()?
darkmist255 darkmist255

2012/1/19

#
First question (which will be helpful for much), how would I go about printing out variables? I have yet to attempt this in Java.
Also why do you take the radian output of atan2(), convert it to degrees, and then re-convert it back into radians again before using sin() and cos()?
Forgive me for anything with radians and degrees (I googled what radians are, I sort of understand a bit). So then, if atan2() outputs radians, then the dividing by PI part is the conversion to degrees, correct? That makes 2 conversions pointless. So would I want something more like:
angle = ((int)(Math.atan2(target.getY()-getY(),target.getX()-getX())));  
  
            xMove = (Math.cos(angle));  
            yMove = (Math.sin(angle)); 
Builderboy2005 Builderboy2005

2012/1/19

#
To print out any text to the Java console, simply do System.out.println(text); As for your angle, remember that radians is a number from 0 to 6.283185... (2Pi). So your code is almost correct, except angle should be a double for the much needed precision. If you use an integer, you only have 7 possible angles from 0 to 2Pi that you can represent. Instead, you code would look like so:
double angle = Math.atan2(target.getY()-getY(),target.getX()-getX());
xMove = Math.cos(angle);
yMove = Math.sin(angle);
This may or may not fix your problem however, but I urge you to use System.out.println() to see what your variables are at specific times to see where the problem is arising.
darkmist255 darkmist255

2012/1/19

#
What really is atan2() vs atan() vs tan() (if there is all 3)? And I'll try this out right once I have time :D!
kiarocks kiarocks

2012/1/19

#
No idea but there is all three.
Builderboy2005 Builderboy2005

2012/1/19

#
tan() computes the tangent of an angle, whereas atan() computes the arctangent of an angle. You may or may not know what tangent and arctangent mean if you haven't been in trigonometry, but it has to do with triangles. Look it up if you are interested. If you are clever, you can abuse arctangent to get this nice pointing effect that you desire, but it requires some extra code accounting for special cases. This pointing effect is done so frequently that Java included the method atan2() to do the extra math for you.
nccb nccb

2012/1/19

#
@darkmist255: The short version in Greenfoot terms is: tan: converts angle into fraction which is y/x (i.e. y divided by x) atan: converts y/x fraction back into an angle. This is problematic because -5/-3 produces the same result as 5/3, so two different directions get converted back into the same angle. atan2: converts y and x back into an angle, taking care of the above problem. And also: sin: converts angle into y (then multiply by movement speed) cos: converts angle into x (then multiply by movement speed) tan and atan get tricky generally, because if x is 0, the fraction becomes infinity. Usually, you use atan2 to go from x,y into angle, and sin and cos to go from angle to x,y. As for radians/degrees: degrees are units that we invented, dividing a circle from 0 to 360. This was an arbitrary choice and it could just as easily be 0 to 1, or 0 to 100 or whatever. Radians chooses 0 to 2*pi (aka tau) -- this may seem odd to have an irrational number as the upper bound, but it is chosen to simplify a lot of calculations involving angles. The one that springs to mind is length/area of an arc (see wikipedia) but I think there are several more.
davmac davmac

2012/1/19

#
this may seem odd to have an irrational number as the upper bound, but it is chosen to simplify a lot of calculations involving angles. The one that springs to mind is length/area of an arc (see wikipedia) but I think there are several more.
In particular, there are series for cos, sin and various other trigonometric functions which are most naturally expressed when the argument is in radians. Eg: sin(x) = x - x^3/3! + x^5/5! - x^7/7! + .... etc (Where 'x^y' means x to the power of y, and 'z!' means z factorial, e.g. 5! = 5 * 4 * 3 * 2 * 1).
darkmist255 darkmist255

2012/1/19

#
Thanks :D! Much clearer now, and I actually understand radians a lot better than before (did some googling). Wonder why we won't learn that until second semester of Grade 12? Now to apply this to my code.
darkmist255 darkmist255

2012/1/19

#
Okay, so I redid that section of the code (MUCH tidier now) and I have a new problem with the moveDouble() method.
    public void moveDouble()
    {
        yMove = 3.63; //these are here as overrides to test if this alone works
        xMove = 2.4;
        
        realXloc = realXloc + (xMove * xMomentum); //momentum is just 1 for testing
        realYloc = realYloc + (yMove * yMomentum);

        // I did println for the double locations and the Y value wasn't even changing?
        
        setLocation((int)realXloc, (int)realYloc);
        
        if(hitBoundary())
        {
            getWorld().removeObject(this);
        }
    }
When I do
            angle = (Math.toDegrees(Math.atan2(target.getY()-getY(),target.getX()-getX()))); //the toDegrees is just so that I can understand what it's telling me, I only use for checking.
            System.out.println(angle);
It appears to return the right angle every time, so I guess now it comes down to this question: Once I get either the angle or the radian value for this, how would I got about moving the actor along the hypotenuse of the imaginary triangle formed? PS: Just to be sure you guys know, I'm not just looking for an answer so I don't have to think for myself; I'm attempting to learn it so I can apply the knowledge to similar situations.
darkmist255 darkmist255

2012/1/19

#
BAKLJGHASEILTHGWKLIHRGTFAKLWERHTASKLERTH (<--- my reaction) I just learned a new thing about declaring variables. If I say "double yMomentum, xMomentum = 1;" Only xMomentum is 1; yMomentum is 0. I set them on different lines, each at 1. Works perfectly now :D. Hehe... hehehe....
davmac davmac

2012/1/19

#
My suspicion is that yMomentum is 0 (or is a very small value). You should print out all the values, just to check: yMove, xMove, yMomentum, xMomentum, realXloc and realYloc before adjustment, realXloc and realYloc after adjustment. I'm sure that will give you the answer! (Edit: ok, you found out the answer yourself first - well done!).
There are more replies on the next page.
1
2