Page 1 of 1

Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 12:52
by Erik
Does anyone have an idea how to make a units turret compensate for
the units turning in order to give the illusion of working independant of it?

What i want is basically a chassis that can move and turn in any direction while the turret stays focussed at the target.

Re: Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 13:19
by Beherith
Im not sure I understand the question, since I believe almost all tanks do this in spring.

Re: Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 13:46
by Erik
I have a turret, i have a chassis.

If the chassis piece turns the turret piece will turn the same in addition to its own turning.

If i now have a fast turning unit with a slow turing turret usually a turn of the vehicle will cause it to loose aim.

What i want now is that the turret works totally independant of the chassis so the chassis may turn in one direction for movement while the turret isn't affected at all, basically like floating over the chassis and not beeing attached to it.

I guess i could work it out if i were to find out how much the unit turned in worldspace and then counterbalance that movement by rotating the turret piece in the other direction.


I reduced my Problem to a simpler one: Can i turn a unit piece by a set angle, for example 90┬░ left without knowing what the result will be?
OR
Can i get a pieces current rotation? Wiki says the function for this is broken...

Re: Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 14:52
by knorke
real world tanks keep their gun pointed at the target now matter how the chasis rotates or tilts.
ie http://www.youtube.com/watch?v=JTq2D ... page#t=18s
Erik wrote:I guess i could work it out if i were to find out how much the unit turned in worldspace and then counterbalance that movement by rotating the turret piece in the other direction.
i think that should work.
could also be used for hot air ballons that do not rotate when they change direction, radar dishes on vehicles and other stuff.

Something similiar:
I tried to make units aim at units that are in LOS but not in weapon range yet. Because it looks kind of silly if a tank drives towards an enemy without pre-aiming.
Gave them a long range weapon that aims the turret but never fires (or is invisible and does no damage, both work)
Kind of worked but had some problems too...

Re: Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 14:55
by FLOZi
Can i get a pieces current rotation? Wiki says the function for this is broken...
If you are using lua;

http://springrts.com/wiki/Animation-LuaCallouts

Spring.UnitScript.GetPieceRotation ( piece ) -> number x, y, z

Re: Turret rotating independant from unit chassis?

Posted: 03 Sep 2011, 15:27
by knorke
gun always aimed "downwards", no matter how chasis rotates:
Image

Code: Select all

function updateheading()
	while (true) do
		lastheading = currentheading
		currentheading = Spring.GetUnitHeading(unitID)
		deltaheading = lastheading - currentheading		
		Sleep (200)

	local _,turretDir,_ = Spring.UnitScript.GetPieceRotation (turret)--pro tip: do this for tilting axis too
	deltaheading = deltaheading * math.pi / 32768
	Turn (turret, y_axis, (deltaheading+turretDir), math.rad(180))
	end	
end
needs stuff to keep unit from doing that when it is actually aiming but thats just the principle.
was thinking to make it toggleable by on/off button.

Re: Turret rotating independant from unit chassis?

Posted: 04 Sep 2011, 14:08
by Erik
Working solution with one small bug.
Implemented here as a hovercraft unit so the turret is basically the chassis, the unit can move around a target and stay focussed at all times.
For the tank variant replace "chassis" by a centered turret.

Feel free to use it but if you do any improvements please post them.

Code: Select all

local chassis = piece "chassis"
local flare1 = piece "flare1"

local SIG_RESTORE=2
local SIG_TURRET=4

local attack_mode=0 --will the unit turn turret along movement or focus on the enemy?

--constant for converting unitheadings to radians
local conversion= 2*3.14159/(2^16)
local attack_heading=conversion*Spring.GetUnitHeading(unitID)


local function restore()
	Signal(SIG_RESTORE)
	SetSignalMask(SIG_RESTORE)
	local unit_dir=0

	Sleep(5000)
	--slowly restore to original settings
	while(true) do
	  unit_dir=Spring.GetUnitHeading(unitID)
	  if (attack_heading<0.03+conversion*unit_dir and attack_heading>-0.03+conversion*unit_dir) then 
	    attack_heading=conversion*unit_dir

	    attack_mode=0   		
	  end
	  --TODO: take into consideration shortest way, as it is now sometimes the
	  --longer turn is taken making the poor driver pretty sick xD
	  if attack_heading>=0.03+conversion*unit_dir then attack_heading=attack_heading-0.03 end
	  if attack_heading<=-0.03+conversion*unit_dir then attack_heading=attack_heading+0.03 end
	  Sleep(60)
	end
end 

local function turret()
	Signal(SIG_TURRET)
	SetSignalMask(SIG_TURRET)

	local unit_dir=Spring.GetUnitHeading(unitID)

	local diff_dir=0

	Sleep(180)
	while(true) do
	  if (attack_mode==1) then 
	   unit_dir=Spring.GetUnitHeading(unitID)

	   --turn the chassis piece to exactly the needed position
	   Turn(chassis, y_axis,attack_heading-conversion*unit_dir)

	   Sleep(30)
	 end

	 --normal behaviour when not in combat situation
	 if (attack_mode==0) then Sleep(30) end	
	end 
end 


function script.Create()
 StartThread(turret)
end

function script.Killed(recentDamage, maxHealth)
	Explode(chassis, SFX.SHATTER) 	
end

function script.StartMoving ()
  Move(chassis, y_axis, 20,9)
end

function script.StopMoving ()

end

function script.AimFromWeapon1() 
	return flare1
end

function script.QueryWeapon1()
	return flare1
end

function script.AimWeapon1( heading, pitch )
	Signal(SIG_RESTORE)
	Signal(1)
	SetSignalMask(1)	
	attack_mode=1
	--aiming animation


	local unit_dir=Spring.GetUnitHeading(unitID)

	while(true) do
	  unit_dir=Spring.GetUnitHeading(unitID)
	  if (attack_heading<0.10+conversion*unit_dir+heading and attack_heading>-0.10+conversion*unit_dir+heading) then
	    --fire shot ! 
	    StartThread(restore)
	    return true
   		
	  end

	  --TODO: take into consideration shortest way, as it is now sometimes the
	  --longer turn is taken 
	 -- Spring.Echo("attack heading",attack_heading)
	 -- Spring.Echo("wanted heading",conversion*unit_dir+heading)
	  if attack_heading>=0.10+conversion*unit_dir+heading then 
	     attack_heading=attack_heading-0.10 
	  end 
	  if attack_heading<=-0.10+conversion*unit_dir+heading then 
             attack_heading=attack_heading+0.10 
	  end

	  Sleep(60)
	end

	--attack_heading=2*3.1415926 --this would be a full circle


	StartThread(restore)
	return true

end

Re: Turret rotating independant from unit chassis?

Posted: 04 Sep 2011, 18:29
by knorke
cool :-)
2 small things:
Signal(1)
SetSignalMask(1)

why not use the constants here?

In function turret() does the Sleep(180) before the loop have a use?

Re: Turret rotating independant from unit chassis?

Posted: 04 Sep 2011, 19:05
by Erik
nope the sleep has no use, mostlikely an artifact of earlier attemts on this.
As for the constant that might aswell be called SIGNAL_AIM or anything.

Re: Turret rotating independant from unit chassis?

Posted: 06 Sep 2011, 13:23
by Erik

Code: Select all

--Define the wheel pieces

local wheel_speed = math.rad(180)
--Define the pieces of the weapon
local chassis = piece "chassis"
local turret = piece "turret"
local guns = piece "guns"
local flare1 = piece "flare1"
local flare2 = piece "flare2"
local active_barrel = 1		--the barrel that the next shot will be fired from
local number_of_barrels = 2		--how many barrel there are in total

local SIG_AIM=1
local SIG_RESTORE=2
local SIG_TURRET=4
--constant for converting unitheadings to radians
local conversion= 2*3.14159/(2^16)
local attack_heading=conversion*Spring.GetUnitHeading(unitID)

local attack_mode=0 --will the unit turn turret along movement or focus on the enemy?


local function restore()
	Signal(SIG_RESTORE)
	SetSignalMask(SIG_RESTORE)
	local unit_dir=0
	local turnspeed=0.06 --the speed at which the turett will turn


	Sleep(5000)
	--slowly restore to original settings
	while(true) do
	  unit_dir=Spring.GetUnitHeading(unitID)
	  --reduce attack_heading & wanted_heading to a number between 0 and 2*PI
	  if (attack_heading<0) then 
	     attack_heading=attack_heading+2*3.14159 
	  end 	 
	  if (attack_heading>2*3.14159) then 
	     attack_heading=attack_heading-2*3.14159 
	  end 	 

	  local wanted_heading=conversion*unit_dir
	  if (wanted_heading<0) then 
	     wanted_heading=wanted_heading+2*3.14159 
	  end 	 
	  if (wanted_heading>2*3.14159) then 
	     wanted_heading=wanted_heading-2*3.14159 
	  end 

	  local dist1=0
	  local dist2=0

	  --check if at original position
	  --calculate distance from desired rotation
	  dist1=attack_heading-wanted_heading
	  if (dist1<0) then dist1=-dist1 end
	  dist2=(2*3.14159-attack_heading)+wanted_heading
	  if (dist2<0) then dist2=-dist2 end

	  if (dist1<1.5*turnspeed or dist2<1.5*turnspeed) then 
	    attack_heading=wanted_heading
	    attack_mode=0 --return to normal mode where turret turns along chassis
	  end


	  if attack_heading>wanted_heading then 
	     dist1=attack_heading-wanted_heading
	     dist2=(2*3.14159-attack_heading)+wanted_heading
	     if (dist1 >= dist2) then 
	      attack_heading=attack_heading+turnspeed
	      wanted_heading=-100 --in order not to trigger the 2nd one aswell
	     end
	     if (dist1 < dist2) then 
	      attack_heading=attack_heading-turnspeed
	      wanted_heading=-100 --in order not to trigger the 2nd one aswell
	     end
	  end 

	  if attack_heading<wanted_heading then 
	     dist1=wanted_heading-attack_heading
	     dist2=(2*3.14159-wanted_heading)+attack_heading
	     if (dist1 >= dist2) then 
	      attack_heading=attack_heading-turnspeed
	     end
	     if (dist1 < dist2) then 
	      attack_heading=attack_heading+turnspeed 
	     end
	  end


	  Sleep(60)
	end
end 

local function manageturret()
	Signal(SIG_TURRET)
	SetSignalMask(SIG_TURRET)

	local unit_dir=Spring.GetUnitHeading(unitID)

	local diff_dir=0


	while(true) do
	  if (attack_mode==1) then 
	   unit_dir=Spring.GetUnitHeading(unitID)

	   --turn the turret piece to exactly the needed position
	   Turn(turret, y_axis,attack_heading-conversion*unit_dir)

	   Sleep(30)
	 end

	 --normal behaviour when not in combat situation
	 if (attack_mode==0) then Sleep(30) end	
	end 
end 


function script.Create()
 StartThread(manageturret)	
end

----driving animation
function script.StartMoving()
	--Turn(guns, x_axis, 0,0.1)
end

function script.StopMoving()

end

----aimining & fire weapon
function script.AimFromWeapon1() 
	return turret
end

function script.QueryWeapon1()
	if (active_barrel == 1) then return flare1 end
	if (active_barrel == 2) then return flare2 end
end

function script.AimWeapon1( heading, pitch )
	Signal(SIG_RESTORE)
	Signal(SIG_AIM)
	SetSignalMask(SIG_AIM)	
	attack_mode=1
	Turn(guns, x_axis, -pitch,0.5)
	--aiming animation
	local turnspeed=0.06 --the speed at which the turett will turn

	local unit_dir=Spring.GetUnitHeading(unitID)

	while(true) do
	  unit_dir=Spring.GetUnitHeading(unitID)


	  --reduce attack_heading & wanted_heading to a number between 0 and 2*PI
	  if (attack_heading<0) then 
	     attack_heading=attack_heading+2*3.14159 
	  end 	 
	  if (attack_heading>2*3.14159) then 
	     attack_heading=attack_heading-2*3.14159 
	  end 	 
	  --Spring.Echo("attack heading",attack_heading)

	  local wanted_heading=conversion*unit_dir+heading
	  if (wanted_heading<0) then 
	     wanted_heading=wanted_heading+2*3.14159 
	  end 	 
	  if (wanted_heading>2*3.14159) then 
	     wanted_heading=wanted_heading-2*3.14159 
	  end 
	  --Spring.Echo("wanted heading",wanted_heading)


	  local dist1=0
	  local dist2=0

	  --check if shot can be fired
	  --calculate distance from desired rotation
	  dist1=attack_heading-wanted_heading
	  if (dist1<0) then dist1=-dist1 end
	  dist2=(2*3.14159-attack_heading)+wanted_heading
	  if (dist2<0) then dist2=-dist2 end

	  if (dist1<1.5*turnspeed or dist2<1.5*turnspeed) then 
	    attack_heading=wanted_heading
	    --fire shot ! 
	    StartThread(restore)
	    return true
	  end


	  if attack_heading>wanted_heading then 
	     dist1=attack_heading-wanted_heading
	     dist2=(2*3.14159-attack_heading)+wanted_heading
	     if (dist1 >= dist2) then 
	      attack_heading=attack_heading+turnspeed
	      wanted_heading=-100 --in order not to trigger the 2nd one aswell
	     end
	     if (dist1 < dist2) then 
	      attack_heading=attack_heading-turnspeed
	      wanted_heading=-100 --in order not to trigger the 2nd one aswell
	     end
	  end 

	  if attack_heading<wanted_heading then 
	     dist1=wanted_heading-attack_heading
	     dist2=(2*3.14159-wanted_heading)+attack_heading
	     if (dist1 >= dist2) then 
	      attack_heading=attack_heading-turnspeed
	     end
	     if (dist1 < dist2) then 
	      attack_heading=attack_heading+turnspeed 
	     end
	  end


	  Sleep(60)
	end

	--attack_heading=2*3.1415926 --this would be a full circle


	StartThread(restore)
	--return true

end

Should be bug free now and taking the shortest of both turns.