Count object rotation turns

This would be a long post with question in the end.

Hi I need to rotate object via player input (mouse for now) and count rotation turns (or spins). Object could be rotated in any direction - clockwise (cw) or counterclockwise (ccw).

For base for movement I’ve taken this example - britzl/defold-orthographic. Shout out to author, his examples always helps me a lot =)

In update() function of the hitman/player script I add something like this to count rotation turns when player hold “mouse button 1”:

-- [hitman.script](https://github.com/britzl/defold-orthographic/blob/master/example/shared/objects/hitman.script)
local function process_spin(self, angle)
	-- transform angle from rad to degree and normilize to 0 - 360 
	angle = angle * 180/3.14159
	if angle < 0 then
		angle = 360 + angle
	end

	-- set initial values on grab - when the player click mouse button 1
	if not self.grab then
		self.grab = true
		self.initial_angle = angle
	end

	-- get the current angle of the object
	local current_angle = angle

	-- calculate the difference between the current and initial angles
	local angle_diff = current_angle - self.initial_angle
	angle_diff = math.floor(angle_diff)

	-- normalize the angle difference to be between -180 and 180 degrees
	if angle_diff > 180 then
		angle_diff = angle_diff - 360
	elseif angle_diff < -180 then
		angle_diff = angle_diff + 360
	end

	-- check and set rotation direction: cw or ccw
	if self.rotation_direction == 0 then
		if angle_diff < 0 then
			self.rotation_direction = 1
		elseif angle_diff > 0 then
			self.rotation_direction = -1
		end
	end
	
	-- normilize rotation to 0 - 360 degree
	if angle_diff < 0 then
		angle_diff = 360 + angle_diff
	end

	-- count turns
	if (self.rotation_direction == 1 and angle_diff <= 5 and angle_diff > 0) 
	or (self.rotation_direction == -1 and angle_diff >= 355) then
		self.turn_count = self.turn_count + 1
		self.initial_angle = angle
	end
end

function update(self, dt)
	...
	-- line 27

	if self.input[hash('touch')] then
			process_spin(self, angle)
		elseif self.grab then
			self.grab = false
			self.last_diff = 0
			self.rotation_direction = 0
	end
	...
end

And this code works at first. The bug is when player change rotation direction while holding “mouse button 1”. So I add check for rotation direction in process_spin function (don’t like how it looks, need to be cleared):

-- check change in dirrection
-- this if statement changed a lot, but always has some bugs...
	if self.last_diff ~= 0 and (self.last_diff - angle_diff) ~=0
 	and ((angle_diff ~= 0 and self.rotation_direction == -1) or (self.rotation_direction == 1)) then
 		local new_direction = 0
 		if (self.last_diff - angle_diff) > 0 then
 			new_direction = 1
 		elseif (self.last_diff - angle_diff) < 0 then
 			new_direction = -1
 		end
 
 		if new_direction ~= self.rotation_direction then
 			self.grab = false
 			self.rotation_direction = 0
 			self.initial_angle = angle
 			self.last_diff = 0
 		end

	if self.last_diff ~= angle_diff then
		self.last_diff = angle_diff
	end

And this part gave me a lot of issues, when player change direction on early start around 0 degree. When one start rotate in one direction from angle_diff = 0 to angle_diff = 1 degree and drastically changes the direction to, say, angle_diff = 358 degree. Code go crazy on this 1 - 358 degree change, because it looks like very quick full circle =)

After some playing with if statement I thought that I’m going a wrong way and I need to deep dive into quaternion topic (never used them before). Long story short I came up with this code:

-- [hitman.script](https://github.com/britzl/defold-orthographic/blob/master/example/shared/objects/hitman.script)
-- line 24
local rotation = vmath.quat_rotation_z(angle)
local rot_diff = vmath.conj(go.get_rotation()) * rotation
local direction = math.floor(rot_diff.z * 100)
-- direction < 0 - clockwise; direction > 0 - counterclockwise.
go.set_rotation(rotation)

This code seems to be working, but I’m not sure that it is optimal for such a task. Quat conjugate and multiply could be heavy to calculate… Have not done performance testing yet.

Maybe I could get rotation direction in more easy or efficient way? In fact when I set new rotation go.set_rotation(rotation) object rotate in right direction, “closest direction”. So engine already know it would be cw or ccw. Maybe I can get this info without additional calculations?

I’m not entirely sure I fully understand the problem, but it feels like you are overcomplicating it. Here’s what I’d try:

https://defold.com/codepad/#?c=#cp_sprite&s1=GYVwdgxgLglg9mABDMMoAoDOBTANsASgCgBIAW0wHMA6ABzkwwCJqmAaRJgQwgEcQYAJ2wB9FLRBQRwOBBCYmxEjnzUoIQWBEQ44KIgC8iAAykVwaoLhQusBCIAmQ7NHhIjp5XgtcwlXNiGJkTYYA5ERKCQdkggtA622FjeHA5QxIiZiAC02cjAiFY2MY7OrgjImCaIAO6BXMKIYNaF1rYolBzYAB5oiHEJUEkEiNgNuACeRFn5iOaWbSVOwuXuHohQABah0zNZwuqau5mh4TPHOXlb9WS6YPpwBUXtFVBwiFy0tJMXuLJcuFaxTcIi4tz0QQAjMZjIgAFSINIXC65D4OBwbbYfcH3RCPIEvJBvD4QORkEC4RIY54xC7zHhkilUkQ0txBemkkDkylDBwsxZsgDUBJKYLuUGRM1Rmy4ADdAnURdgMVxEKBcIDDmAAPwXGAFDmMnnK/nAioAPkQAGYAGyw65gC4zQ1cpm802E9neagM13Gvmsip5W2mPZZeZa7Tir2qSM6CHCyFOrK0QQoDARjRaeP3DJZU6SrKommBa7IMhcSjYOne3z+QJGZKqOsBRDCwNaMUQhHzDulFYxEYAUmtdsLmUocGoOGYrA4TGwFOwgmoAC92HNa34AsQCxEoqs8VpxJIm8AODwSjAHBfVnnMvqSVeMQ7kw+Cpe3HRhJgcC/to6YZhi63LMh2QShkBzren2ywuDEUJvqMuA4I+n4IJYeBjH+mI7FB0GqLBZQIR4SEFjMe57geCH2MIfxcA4Z7ECEYREEAA==

3 Likes

Sorry, I was not clear of my goal.
Game Object will be moved and rotated by player input. Now it is WASD + mouse, but I want to add controller (gamepad) support in future. Think of it like: you are rotating object around it’s axis using mouse. Here is GIF of how it looks for now:
rotate-demo-1

Well, the example logic of @britzl’s code still appplies imho.
Keeping a separate value with the accumulated angle is the way I would go too.
Yes, you would have to modify it to handle the case of rotating it both ways.

1 Like

Yeah, in my example I rotated on any action press but that can easily be changed:

function on_input(self, action_id, action)
    if action_id == hash("right") then
        if action.pressed then
            self.accumulated_rotation = 0
            self.rotation_direction = 1
        elseif action.released then
            self.rotation_direction = 0
        end
    elseif action_id == hash("left") then
        if action.pressed then
            self.accumulated_rotation = 0
            self.rotation_direction = -1
        elseif action.released then
            self.rotation_direction = 0
        end
    end
end

Thanks for answers.
I’m rotating not by click, but by the mouse move, as in https://github.com/britzl/defold-orthographic/blob/master/example/shared/objects/hitman.script example.
I’m going to rework my script. I’ll post here the result.

1 Like

If you want the rotation of a GO1 to be controlled by the location of another GO2 (which might be controlled by an input action) then you could attach a collision object to GO1, and on_message it. This will do rotation:

local p = message.other_position
local t = go.get_position()
local angle = math.atan2(p.y- t.y, p.x - t.x)
local rot = vmath.quat_rotation_z(angle)
go.set_rotation(rot)

Just needs some tweaking to do the counting.
Edit: Argg, just looked at the soln below, clearly I don’t know what I am talking about!

If you want the rotation of a GO1 to be controlled

I’ve already done it, via example that I mentioned in first post. My initial question was exactly about rotation counting :slight_smile:


I haven’t managed to perform rotation+count as were advised in previous posts. And come up with something that I don’t like, and has bugs )
I’ve prepared my demo here:

Use Mouse to rotate Game Object around it’s axis. Use Left mouse button to start rotation count.

BUT code breaks down to count rotations when you start to use WASD to move object and Mouse to rotate simultaneously. I need another way of finding rotation direction.

1 Like

I’ve reworked it :slight_smile:
Demo in github repo.
Use Mouse to rotate Game Object around it’s axis. Use Left mouse button to start rotation count. Use WASD to move object.
Thanks everyone for tips :handshake:
Would appreciate if someone has advises how to improve my code.

1 Like