Smooth Character turning / Natürlich Charakterdrehung

  • #50, by afrlmeMonday, 06. October 2014, 20:55 10 years ago
    Ah yeah I misunderstood what you meant. I know that quite a few people on here are making side-scrolling adventure games & thus don't need as many walking directions.

    I was thinking more about generating amount of degrees based on amount of walk directions.

    With 2 walk directions there wouldn't be much point in calculating shortest distance really. You might as well just create 2 animations for that - well actually even 1 would work as you would be able to play it forwards or in reverse. You would just need to store destination/actions inside of a table, stop character, play animation & then restore the destination/actions.

    Imperator

    7285 Posts


  • #51, by Lord7evenWednesday, 15. October 2014, 16:53 10 years ago

    A short status update about the script:



    • Modified it further so every character you set in the script has now the ability to turn. (when walking to a new object/point and also on just "turn character to x degrees")
    • The script is fully customizable. You can use different turning steps on characters. Perhaps your main-char turns 45° steps and another character turns only in 90° steps.
    • You can use any number of turn-steps you want. The “algorithm” works in every possible direction.
    • You can use special turning animations (person animations) or just the normal standing animations if you don't have any turn-animation.


    Here is something about the code you might want to know:


    You set the name, directions and the animation names of characters that should turn.
    The script checks if any of the set characters changes his direction and then play the turning animation you set. After the animation is finish it plays the next animation till the character reaches its final direction.

    So why is the script not available here and right now?


    Well there is one problem with the script right now: You can't send your character to a new destination while he is walking to another direction. This is like you set the option "Standardbefehl aktivieren" in the main options of your game to "Nur nach erfolgreicher Ausführung" (I don't know the description in English sorry!)

    There could be also a problem if you set too many characters that should turn because it cycles every character, you set in the script, doing the main loop which means if it takes longer than about 16 milliseconds to loop through all the characters, it could produce a lag in your game. I've tested it only with two characters walking and turning at the same time and also only with 1-frame turning animations in 45° steps. (The car demo I've posted earlier, with two cars.)

    Newbie

    40 Posts

  • #52, by afrlmeWednesday, 15. October 2014, 17:27 10 years ago
    If you break it down into functions & insert the character name into a table with the values & then remove that table when you are done. If done that way then functions triggered should be able to work at same time as other functions already active. Ideally an hook that listens out for character state changes would be very useful - I'm talking about an internal hook/listener.

    Imperator

    7285 Posts

  • #53, by Lord7evenFriday, 17. October 2014, 16:12 10 years ago
    Well I'll have to break it down to what I've got for the moment, since I'll not have much time in the next two months. So here is what I've got. It's not well coded. It could be done with less code I believe but I'm still learning. The script checks now, if the character you've set, is on the actual scene and visible or not, so it should produce a lag if you set too many characters for the smooth turning.

    You have to create animations for each step of turn.
    Let's say you just have 4 walking directions (0°, 90°, 180°, 270°) and you want to make the character turn smoother with 15° turning steps so the character turns from 90° to 105° to 120° till the final direction of let's say 180° is reached. This would mean you create person animations with one frame, named with the direction. (turn_90, turn_105 and so on). You can also name your animations "turn_front_left" or something like that; it doesn't matter because you will set the names of the character in the first part of the script.

    The amount of steps/animations depends on how smooth you want to turn the character not on the characters standing/walking directions but use only 1 frame for each step. You can also set different character with different turning steps. (Description examples in the script.)

    The speed of the turning animation is set by the playtime of each frame in the animation options so each frame in the turn is shown as long as you set it.

    Here are the variables to set: (Definition Script)
    --[[	Set every name of character who should be able to turn smooth
    	Examples: 
    	smt_allChars = {"Car"}
    	smt_allChars = {"Hero", "Car"}
    --]]
    smt_allChars = {"Hero", "Hero2"}
    
    
    --[[	Set the below variable to each direction the character can turn to, starting with the largest number.
    	You can set multiple characters with different amount of direction.
    	Examples: 
    	smt_animation = {Car = {315, 270, 225, 180, 135, 90, 45, 0}}
    	smt_animation = {Hero = {270, 180, 90, 0}, Car = {315, 270, 225, 180, 135, 90, 45, 0}}
    --]]
    smt_animation = {Hero = {315, 270, 225, 180, 135, 90, 45, 0}, Hero2 = {315, 270, 225, 180, 135, 90, 45, 0}}
    
    
    --[[	Names of your turning animations of the character in order of the numbers above.
    	Examples:
    	smt_turningAniNames = {Car = {"character_turn_315", "character_turn_270", "character_turn_225", "character_turn_180", "character_turn_135", "character_turn_90", "character_turn_45", "character_turn_0"}}
    	smt_turningAniNames = {Hero = {"turn_front", "turn_left", "turn_back", "turn_right"}, Car = {"character_turn_315", "character_turn_270", "character_turn_225", "character_turn_180", "character_turn_135", "character_turn_90", "character_turn_45", "character_turn_0"}}
    --]]
    smt_turningAniNames = {Hero = {"character_turn_315", "character_turn_270", "character_turn_225", "character_turn_180", "character_turn_135", "character_turn_90", "character_turn_45", "character_turn_0"}, Hero2 = {"character_turn_315", "character_turn_270", "character_turn_225", "character_turn_180", "character_turn_135", "character_turn_90", "character_turn_45", "character_turn_0"}}
    

    Here is the main code: (Definition Script)
    -- Smooth Character Turning Script 
    -- (c) Simon Scheckel 2014 - Visionaire Studio Engine
    -----------------------------------------------------------
    -- turnClockwise() and aditional changes by Pascal Lüthi
    -- Special Thanks to AFRLme
    -----------------------------------------------------------
    --[[
    	---- Main Code ----
    --]]
    smt_direction = {}
    smt_dest = {}
    smt_destobj = {}
    smt_actionc = {}
    smt_last_position = {}
    smt_anim = {}
    smt_nextdir = {}
    smt_clockwise = {}
    smt_turnWhileStanding = {}
    smt_NoTurn = {}
    
    -- Set the variables above for every character that has the ability to turn
    for i = 1, #smt_allChars do
    	local char = getObject("Characters[" ..  smt_allChars[i] .. "]")
    	smt_direction[i] = char.Direction
    	smt_dest[i] = char.Position
    	smt_destobj[i] = char.DestinationObject
    	smt_actionc[i] = char.ActionCharacter
    	smt_last_position[i] = smt_direction[i]
    	smt_anim[i] = 0
    	smt_nextdir[i] = 0
    	smt_clockwise[i] = false
    	smt_turnWhileStanding[i] = true
    	smt_NoTurn[i] = true
    end
    
    -- Returns the turning distance between a and b in degrees
    function angleDist(a,b)
    	return math.min(math.min( math.abs(a + 360 - b), math.abs(a - 360 - b)), math.abs(a - b))
    end
    
    -- Returns true if the fastes turn is a clockwise turn, otherwise false will be returned
    function turnClockwise(startDirection, endDirection)
    	if startDirection == 0 then startDirection = 360 end
    	if endDirection == 0 then endDirection = 360 end
    	if startDirection > 180 then
    		if endDirection > startDirection - 180 and endDirection < startDirection then
    			return true
    		else
    			return false
    		end
    	else
    		if endDirection > startDirection + 180 or endDirection < startDirection then
    			return true
    		else
    			return false
    		end
    	end
    end
    
    -- Returns the next frame of the turning animation
    function getNextTurningFrame(actualdir, maxdir, clockwise)
    	local nextFrame = 1
    	if clockwise then
    		if actualdir ~= maxdir then
    			nextFrame = actualdir + 1
    		end
    	else
    		if actualdir ~= 1 then
    			nextFrame = actualdir - 1
    		else
    			nextFrame = maxdir
    		end
    	end
    	return nextFrame
    end
    
    -- Checks if the person is in the actual scene or not
    function isPersonInScene(person)
    	if person.Active then
    		local scene = getObject("Characters[" ..  person:getName() .. "].CharacterScene")
    		local playerScene = getObject("Game.GameCurrentCharacter.CharacterScene")
    		if scene:getName() == playerScene:getName() then
    			-- print(person:getName() .. " is active!")
    			return true
    		else
    			-- print(person:getName() .. " is not on actual scene!")
    			return false
    		end
    	else
    		-- print(person:getName() .. " is inactive!")
    		return false
    	end
    end
    
    -- Main loop event handler
    function smoothTurningMainLoop()
    	for k = 1, #smt_allChars do		-- Loop every Character set to turn
    		local c = getObject("Characters[" ..  smt_allChars[k] .. "]")
    
    		if isPersonInScene(c) then	-- is character visible?
    			local newstate = c.AnimState
    			local newdir = c.Direction
    			local animTable = smt_animation[c:getName()]
    
    			-- After a turning animation is finished
    			if smt_anim[k] ~= 0 and smt_anim[k]:getId().id == -1 then
    				smt_nextdir[k] = getNextTurningFrame(smt_nextdir[k], #animTable, smt_clockwise[k])
    				c.ActionCharacter = smt_actionc[k]
    				if smt_nextdir[k] == smt_last_position[k] then -- turning complete?
    					smt_anim[k] = 0
    					smt_NoTurn[k] = true
    					if smt_turnWhileStanding[k] == false then	-- end turn in a walking progress
    						c.DestinationObject = smt_destobj[k]	-- set the original destination object
    						c.Destination = smt_dest[k]	-- set the original destination coordinates
    					end
    					startAnimation(getObject("Characters[" ..  smt_allChars[k] .. "].CharacterCurrentOutfit.OutfitCharacterAnimations[" .. smt_turningAniNames[c:getName()][smt_nextdir[k]] .. "]"))
    					print("end turn with: " .. smt_turningAniNames[c:getName()][smt_nextdir[k]])
    					print("")
    				else
    					-- Set the next turning segment
    					print("play turn: " .. smt_turningAniNames[c:getName()][smt_nextdir[k]])
    					smt_anim[k] = startAnimation(getObject("Characters[" ..  smt_allChars[k] .. "].CharacterCurrentOutfit.OutfitCharacterAnimations[" .. smt_turningAniNames[c:getName()][smt_nextdir[k]] .. "]"))
    				end
    			end
    
    			-- Does character change direction?
    			if smt_direction[k] ~= newdir and smt_NoTurn[k] then
    				--[[ 	Finding best frame for the turning animation based on the smt_animation variable.
    					bestspos is then the frame of the direction the character is facing before the turn.
    				--]]
    				local bestspos = 1
    				local bestdist = 360
    				for i = 1, #animTable do
    					local dist = angleDist(animTable[i], smt_direction[k])
    					if  dist < bestdist then
    						bestdist = dist
    						bestspos = i
    					end
    				end
    
    				--[[ 	Finding best frame for the turning animation based on the smt_animation variable.
    					bestlpos is then the frame of the direction the character is facing after the turn.
    				--]]
    				local bestlpos = 1
    				bestdist = 360
    				for i = 1, #animTable do
    					local dist = angleDist(animTable[i], newdir)
    					if  dist < bestdist then
    						bestdist = dist
    						bestlpos = i
    					end
    				end
    
    				-- check if the first direction of the turn is different to the last direction of the turn
    				local turn = false
    				if bestspos ~= bestlpos then
    					print("char name: " .. c:getName())
    					if newstate == eWalkAnim then	-- turning while walking to a point
    						print("turning while walking")
    						smt_dest[k] = c.Destination	-- backup the original destination coordinats
    						smt_destobj[k] = c.DestinationObject	-- backup the original destination object
    						c.Destination = c.Position	-- set character destination to it's actual position so he doesn't float away while turning			
    						smt_turnWhileStanding[k] = false
    						turn = true
    					else	-- turning while standing
    						print("turn while standing")
    						smt_turnWhileStanding[k] = true
    						turn = true
    					end
    				end
    
    				if turn then	-- should the character turn?
    					smt_NoTurn[k] = false
    					smt_actionc[k] = c.ActionCharacter
    					smt_last_position[k] = bestlpos	-- backup last direction of the turn to start the right walking animation
    					smt_nextdir[k] = bestspos	-- backup the starting direction of the turn
    					smt_clockwise[k] = turnClockwise(smt_direction[k], newdir)	-- check to turn clockwise or not
    					print("start the turn with: " .. smt_turningAniNames[c:getName()][bestspos])
    					smt_anim[k] = startAnimation(getObject("Characters[" ..  smt_allChars[k] .. "].CharacterCurrentOutfit.OutfitCharacterAnimations[" .. smt_turningAniNames[c:getName()][bestspos] .. "]"))
    				end
    			end
    			
    			smt_direction[k] = newdir
    		end
    	end
    end
    
    registerEventHandler("mainLoop", "smoothTurningMainLoop")
    


    You can download a demo project with two cars if you are not sure what's to do or just want to look at it.
    In the demo you can send the cars to different positions with the buttons F1 - F5 (main car which is playable) and F6 - F10 (2nd NPC car)

    Newbie

    40 Posts

  • #54, by marvelTuesday, 08. September 2015, 16:36 9 years ago
    I'm about to check out this script. Great demo by the way. smile

    Unfortunately this only works with single frames and not with real turn-animation because the animation needs to be mirrored (if the character turns to the opposit direction). Any solution for this? smile

    Key Killer

    599 Posts

  • #55, by afrlmeTuesday, 08. September 2015, 16:47 9 years ago
    I think the best solution would be to use a single animation & have it force the frames / direction of that animation depending on the target direction from the starting direction. Easier said than done though.

    Ideally if you want a really really natural looking turn then you would need animations for each target direction from each starting direction. That's a lot of animations mind.

    P.S: by natural looking I'm referring to animating the actual rotation movement of the characters body / legs / feet & not just simply rotating a static (idle animation) of the character around. https://www.youtube.com/watch?v=dQo-XzNByNY

    Imperator

    7285 Posts

  • #56, by gustyTuesday, 08. September 2015, 18:02 9 years ago
    I think something like that is convenient enough:
    https://www.youtube.com/watch?v=wMnDbygZ3VQ

    Forum Fan

    159 Posts

  • #57, by afrlmeTuesday, 08. September 2015, 18:16 9 years ago
    That is what I was talking about. It's not as detailed as I said, but you can still see the feet rotating slightly instead of just rotating the idle animation or snapping between the idle frame directions.

    Imperator

    7285 Posts

  • #58, by marvelTuesday, 08. September 2015, 18:26 9 years ago
    Maybe you could solve this with a good animation. smile I'm currently doing some tests with a new version of the script and it seems to work. But need some more time to test, though...

    Key Killer

    599 Posts

  • #59, by sebastianTuesday, 08. September 2015, 18:53 9 years ago
    i would love to see the new Version in a demovideo smile

    Thread Captain

    2346 Posts

  • #60, by TinTinSunday, 31. January 2016, 14:07 9 years ago
    Sorry for old topic
    It's nice script and helpful.
    How can I get optimize this script for 12 directions ? Is it possible ?

    Forum Fan

    196 Posts