Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (808)
Games in Android Showcase (239)
games submitted by our members
Games in WIP (872)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Issue with sprite rotation distortion and need advice on speed of rotation  (Read 40771 times)
0 Members and 1 Guest are viewing this topic.
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Posted 2015-06-17 22:48:27 »

Hi all.

I've created a turret that follows the closest enemy within a specified range but i'm having a few issues. The first one seems that the sprite gets distorted when it rotates, as if then batch is trying to make it look better? Or something. Wondering if anyone has come across this (I'm using pixel art).

The other issue I have is that I want the tower to rotate at a set speed, at the moment it simply follows the enemy at whatever speed it is going. I thought it might be cool if different turrets went at different speeds

Also, at the moment, if no enemy is selected after the turret has been following an enemy, it will snap back to it's original position which looks bad. I was thinking that I could use something like this to solve it:

1  
2  
3  
4  
5  
6  
7  
if(enemyToTarget != null){
 // Rotate to face enemy
}else{
   if(turret.getAngle != 180){
        turret.setRotation(angle--);
   }
}


Obviously i'd have to check if the tower is facing up/down or what angle to actually face at but I feel this should work

Hey, you! Back to work
Offline chrislo27
« Reply #1 - Posted 2015-06-17 23:06:56 »

If you could post a screenshot of the rotation that would be helpful.

As for your rotation speed:

Instead of setting the turret's rotation, you could give it a target rotation. If it's tracking an enemy, the target rotation is relative to the enemy. If it's not tracking, the target rotation is whatever it "snaps" back to. Now that you have a target rotation, each turret could have a rotation speed. It'll attempt to change its rotation at some speed it has. The best part about this is that you can change the interpolation type, you could make it spin faster based on how far away it's angled and slow down when it's nearer.

Hope this helps!

Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #2 - Posted 2015-06-17 23:25:19 »

I'll get you a video! Smiley

So what I could do is have something like this for the speed:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
private int moveSpeed = 5;
if(enemyToTarget != null){
     
        double degrees = Math.atan2(enemyToTarget.getyPos() - getY(), enemyToTarget.getxPos() - getX());  
        if(angle != degress){
              setRotation(moveSpeed++)
           }
      }
}else{
// Same thing here but move to default location
}


EDIT: Video (Look around the edge of the turret to see what I mean by distortion)
<a href="http://www.youtube.com/v/OeX0c56i1Sc?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/OeX0c56i1Sc?version=3&amp;hl=en_US&amp;start=</a>

Hey, you! Back to work
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline chrislo27
« Reply #3 - Posted 2015-06-17 23:50:30 »

The "distortion" is simply part of how sprites are rotated. You can't really do much about it.

Your code is setting the rotation to the speed, then incrementing the speed. That would mean your speed would accelerate (rapidly) over time...

What you meant was probably this:
1  
setRotation(getRotation() + (Math.min(moveSpeed, Math.abs(getRotation() - degrees)));


That code will add to the existing rotation either the moveSpeed or the distance (in degrees) away from the target, whichever's smaller. This will combat (ha get it it's a turret sorry) overcompensation. However that code only lets it move clockwise, you'll have to do some more work to check if counter-clockwise would be faster to move.
Offline Jesse

JGO Coder


Medals: 23



« Reply #4 - Posted 2015-06-18 00:02:15 »

By distortion, do you mean the aliasing ('jaggies') around the edge of the turret sprite? In other words, the 'stair step' effect? If so, the first thing to check, I think, is what filtering mode you have set in the rendering API you're using (assuming the API exposes that). If you have something like 'nearest' selected, you could try using linear filtering instead and see if you prefer that look. (chrislo27 posted above while I was typing this; as he alludes to, if you're using 'nearest' filtering deliberately to get a 'pixel art' effect, you may not be able to do much about the aliasing.)

Presumably your latest code sample isn't your actual code, as it has some typos in it. Just as a heads-up though, you have a variable named 'degrees', but unless you're using some other math library, atan2() returns radians. Also, due to floating-point imprecision, you won't want to check that the angle is exactly equal (or unequal) to the target angle; you'll want to use some other method of detecting when the turret has reached its target angle that allows for floating-point error. Lastly, maybe you're doing it that way for a reason, but be careful in general about mixing integer and floating-point math, as it can sometimes lead to unexpected results.

[Edit: typos.]
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #5 - Posted 2015-06-18 00:19:01 »

1  
setRotation(getRotation() + (Math.min(moveSpeed, Math.abs(getRotation() - degrees)));


Oops, I see what I did wrong. For calculating if moving counter-clockwise is faster, I read online that I can use sin(currentAngle - targetAngle) and if it is positive then move clockwise, else move counter-clockwise Smiley. I don't know why this works though xD

By distortion, do you mean the aliasing ('jaggies') around the edge of the turret sprite?

That's the one Smiley I'm Googling around now to see if I can change it

Hey, you! Back to work
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #6 - Posted 2015-06-18 01:05:10 »

Is there some way in which I can check if the tower is at the correct angle? I can't use precision due to floating points and I can't use getRotation() as this has no 360 limit and just increments

Hey, you! Back to work
Offline Jesse

JGO Coder


Medals: 23



« Reply #7 - Posted 2015-06-18 01:20:59 »

Is there some way in which I can check if the tower is at the correct angle?

This may be more than you're interested in, but I'd recommend doing this with vector math. It'll still involve angles, but if you're not already, you'll probably want to get comfortable with expressing these sorts of problems in terms of vector math and transforms, since this will make it easier to solve a lot of other types of problems as well.

For this you'll need a 2-d vector class (which I assume you have available). If it doesn't already have a 'perp' function, you'll need to implement that. The 'perp' function returns a vector perpendicular to the input vector, and can be implemented as (all untested pseudocode):

1  
perp(v) = [ -v.y, v.x ]

Next you'll need a 'perp dot' function, which can be implemented as:

1  
perp_dot(a, b) = dot(perp(a), b)

You'll also need a 'signed angle' function, which can be implemented as:

1  
signed_angle(a, b) = atan2(perp_dot(a, b), dot(a, b))

Once you have these function available (which you may already), you'll need the forward vector for your turret:

1  
forward = [ cos(turret_angle), sin(turret_angle) ]

You'll also need the vector from the turret to the target:

1  
vector_to_target = target_position - turret_position

At this point you can compute the signed angle by which the turret would need to turn to point in the right direction:

1  
angle_to_turn = signed_angle(forward, vector_to_target)

As I think chrislo27 alluded to earlier, each update you would then rotate by min(turn_speed * delta_time, angle_to_turn). The turret can be rotated back to its original orientation similarly if needed. This method should handle stopping at the correct angle automatically, although if you run into any jitter or other artifacts, there are things you can do to fix that.

The above may seem a little complicated (or maybe not), but the vector math for this is really pretty straightforward. In any case, this is how I'd do it.
Offline chrislo27
« Reply #8 - Posted 2015-06-18 02:41:26 »

Is there some way in which I can check if the tower is at the correct angle? I can't use precision due to floating points and I can't use getRotation() as this has no 360 limit and just increments

You can also check using modulo division. Where angle = getRotation() % 360. It will get the remainder of that division. You could also just cast to an int from a float when checking since your game is pixel-arty and doesn't require too much accuracy.
Offline craftm

JGO Coder


Medals: 15
Projects: 1


_Keep Trying


« Reply #9 - Posted 2015-06-18 02:53:40 »

To solve the "distortion" you can apply a filter to the texture, but you will lose the "pixel" style art.

1  
txt.setFilter(TextureFilter.Linear, TextureFilter.Linear);

_ Wink
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline chrislo27
« Reply #10 - Posted 2015-06-18 04:19:55 »

Another overkill solution is to make separate sprites for different rotations. This'll definitely solve your distortion but it's kinda like killing a fly with a sledgehammer.
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #11 - Posted 2015-06-18 13:09:55 »

@Jesse That's really confusing me Huh I've only done addition/normalizing vectors before. Do you know of any good resources where I could learn more in-depth about these functions and maybe trig?. One of the biggest things that's bothering me is trigonometry. I understand sin/cos in basic terms and SOH-CAH-TOA but I have no idea when to use certain functions. For example, it said online that I can use Math.sin(targetAngle - currentAngle) and if the result is negative then I have to turn counterclockwise instead of clockwise. I have no idea why I would use sin here instead of cosine Huh The only trig I've really used is something like the following. Going in a circle
1  
int x = length * Math.cos(angle++), int y = length * Math.sin(angle++)


@Craftm Ah yeah, I noticed. I decided to revert back to the default texture filtering to keep the pixel effect

@chrislo27 Thanks for your help Smiley I haven't been able to test the modulo division yet as i'm working on the rotation of clockwise/counterclockwise but I understand how it works. Also:

It's kinda like killing a fly with a sledgehammer.
This made me snort persecutioncomplex xD

Current video of rotation in action. Few issues with jittering and it's not pointing exactly at the target but it's close!

<a href="http://www.youtube.com/v/bJq0mximX4c?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/bJq0mximX4c?version=3&amp;hl=en_US&amp;start=</a>

Hey, you! Back to work
Offline Jesse

JGO Coder


Medals: 23



« Reply #12 - Posted 2015-06-19 06:42:28 »

@Jesse That's really confusing me Huh I've only done addition/normalizing vectors before. Do you know of any good resources where I could learn more in-depth about these functions and maybe trig?.

If you're getting the behavior you want without having to dive into vector math, then maybe you don't need to worry (much) about vector math right now. It's liable to be useful in the future though. As for references, there are lots of tutorials on vector math online, so a simple search will likely get you started.

Quote
Math.sin(targetAngle - currentAngle) and if the result is negative then I have to turn counterclockwise instead of clockwise. I have no idea why I would use sin here instead of cosine

Assuming I understand the intent of that method, the reason sine is used rather than cosine is that sine is non-negative from 0 to 180 degrees, and negative over the remainder of the range. Cosine on the other hand is non-negative from -90 to 90 degrees and negative over the remainder, so it wouldn't give you the results you want here.

In any case, it looks like you're getting pretty close to the desired behavior in your latest video, so maybe angles is all you need Smiley

Edit: wording.
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #13 - Posted 2015-06-19 14:51:31 »

If you're getting the behavior you want without having to dive into vector math, then maybe you don't need to worry (much) about vector math right now. It's liable to be useful in the future though. As for references, there are lots of tutorials on vector math online, so a simple search will likely get you started.
I'll take a look on YouTube, I know there is a guy who dives really deep into other concepts as well and has over 80 videos on game math. Maybe that will help

Assuming I understand the intent of that method, the reason sine is used rather than cosine is that sine is non-negative from 0 to 180 degrees, and negative over the remainder of the range. Cosine on the other hand is non-negative from -90 to 90 degrees and negative over the remainder, so it wouldn't give you the results you want here.
I.. I think I understand persecutioncomplex

Hey, you! Back to work
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #14 - Posted 2015-06-20 14:22:41 »

Just a quick update for anyone who may come across this thread and have the same issue. So I've been struggling to get the turret to point perfectly at the enemy and I just realised my silly mistake.

This line of code was causing the problem, if you notice, i'm turning the radians into degrees AND I'm adding the additional 90 to the existing radians which is a big mistake:
1  
double r = Math.toRadians(Math.toDegrees(radians + 90) - getRotation());

The following code change fixed the issue as it converts the radians to degrees then adds the 90 degrees as we are now dealing with degrees.

1  
double r = Math.toRadians((Math.toDegrees(radians) + 90) - getRotation());

After this code I then use Math.sin(r) to test if I should rotate clockwise/anticlockwise.

You can find the completed code below Smiley, I still however need to use Math.min() to check if the rotation or degrees is less and then what I should rotate

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
// Get the radians to the enemy to target.
double radians = Math.atan2(enemyToTarget.getyPos() - getY(),
   enemyToTarget.getxPos()- getX());
           
/* Turn our radians to degrees and then everything back into radians. + 90 on the degrees as we want the top of the turret to be the point to face the enemy.
* Then minius the rotation. This will allow us to see if we need to turn clockwise or anti-clockwise using Math.sin()
*/

double r = Math.toRadians((Math.toDegrees(radians) + 90) - getRotation());
           
// If sin(r) is less than 0, we need to rotate anti-clockwise, otherwise we rotate the other way
if(Math.sin(r) < 0){
   setRotation((float) getRotation() + 1);
}else{
   setRotation((float) getRotation() - 1);


Hey, you! Back to work
Offline chrislo27
« Reply #15 - Posted 2015-06-20 15:09:10 »

Glad you figured it out! Maybe you should update the target "direction" only once or a few times per second because trigonometry functions can be slightly computationally expensive (obviously with today's hardware it's practically negligible. You can then update the actual direction by moving it towards the target direction.
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #16 - Posted 2015-06-20 23:19:11 »

So I should use some sort of timer to check if I should recalculate the position to face? Smiley

Edit: And thanks for the help, couldn't do it without you guys Grin I've learnt a lot

Hey, you! Back to work
Offline chrislo27
« Reply #17 - Posted 2015-06-20 23:34:44 »

Does your game run in "ticks"? (as in, there's a tick for every X frames)? If so, you should have a tick counter. To find when you should re-calculate the targets, you could do
1  
totalTicksElapsed % (yourTimeBetweenUpdatesInTicksHere) == 0


Basically if the modulo division of the total tick time and the time between updates is 0, you can update. You could replace 0 with any number within range of the update duration but 0 works for all cases like these.
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #18 - Posted 2015-06-20 23:37:08 »

I use LibGDX so I'm not sure about ticks persecutioncomplex I usually create a float that adds the delta time each update and if the time has passed, it does the code and resets the float

Hey, you! Back to work
Offline chrislo27
« Reply #19 - Posted 2015-06-20 23:42:37 »

I use libgdx as well. I implemented ticks exactly as you said: there's a counter for delta time and when it hits a threshold it subtracts the threshold from the delta time and does a tick update. I need to make it use nanosecond time instead for more precision. As a matter of fact, I'll go do it right now... Lips Sealed
Offline SauronWatchesYou

JGO Ninja


Medals: 33
Projects: 4
Exp: 2 years


Hi there! :)


« Reply #20 - Posted 2015-06-21 21:26:35 »

I use libgdx as well. I implemented ticks exactly as you said: there's a counter for delta time and when it hits a threshold it subtracts the threshold from the delta time and does a tick update. I need to make it use nanosecond time instead for more precision. As a matter of fact, I'll go do it right now... Lips Sealed

Oh I see Smiley is there any reason for the tick implementation over a simple float that adds on the delta time? I'm just asking so I understand correctly. This is what i'm talking about:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
public class Timer{
    private float time;
   
    public void update(){
        time += Gdx.graphics.getDeltaTime();
       
        if(time >= 1f){
             time = 0;
             System.out.println("Time reached");
         }
    }
}

Hey, you! Back to work
Offline chrislo27
« Reply #21 - Posted 2015-06-22 00:49:11 »

It doesn't really matter for singleplayer games, but it matters the most in multiplayer games where you can't spam movement packets 60 times a second. I have to limit it to 20 per second and interpolate the positions in-between frames.

Also, that snippet of code in your reply was essentially my tick counter, albeit with (1f / TICKS where TICKS is 20). I changed it to nano-second time for precision.
Pages: [1]
  ignore  |  Print  
 
 

 
Riven (848 views)
2019-09-04 15:33:17

hadezbladez (5799 views)
2018-11-16 13:46:03

hadezbladez (2605 views)
2018-11-16 13:41:33

hadezbladez (6215 views)
2018-11-16 13:35:35

hadezbladez (1501 views)
2018-11-16 13:32:03

EgonOlsen (4736 views)
2018-06-10 19:43:48

EgonOlsen (5794 views)
2018-06-10 19:43:44

EgonOlsen (3278 views)
2018-06-10 19:43:20

DesertCoockie (4176 views)
2018-05-13 18:23:11

nelsongames (5503 views)
2018-04-24 18:15:36
A NON-ideal modular configuration for Eclipse with JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!