Issue with sprite rotation distortion and need advice on speed of rotation

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:


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

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!

I’ll get you a video! :slight_smile:

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


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)

OeX0c56i1Sc

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:


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.

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.]

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 :). I don’t know why this works though xD

That’s the one :slight_smile: I’m Googling around now to see if I can change it

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

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):

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

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

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

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

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:

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

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

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:

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.

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.

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

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

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.

@Jesse That’s really confusing me ??? 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 ??? The only trig I’ve really used is something like the following. Going in a circle

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 :slight_smile: 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:

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!

bJq0mximX4c

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
[/quote]
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 :slight_smile:

Edit: wording.

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

I… I think I understand :persecutioncomplex:

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:

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.

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

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

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


// 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);

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.

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

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

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


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.

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

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… :-X