Page 2 of 3

Re: STEAL - top down stealth game

Posted: Mon Oct 20, 2014 9:10 am
by Fenrir
Thanks guys!

Doing a devlog about the lighting and line of sight system is in my todolist, I'll let you know as soon as I find some time for it! :)

Re: STEAL - top down stealth game

Posted: Mon Nov 10, 2014 5:36 pm
by Fenrir
Hi guys,

Just wanted to share this gif with you:

Image

It shows all player animations, they were made with Blender, and I made a quick plugin exporting everything as pure LUA tables, something like:

Code: Select all

    sneaking = {
        { name = "AVANT_BRAS_DROIT", keyframes = { location_X = { { time = 0, value = 4.03 }, { time = 9, value = -1.38 }, { time = 18, value = 4.03 }, }, location_Y = { { time = 0, value = -32.83 }, { time = 9, value = -34.15 }, { time = 18, value = -32.83 }, }, location_Z = { { time = 0, value = -3.0 }, { time = 9, value = -3.0 }, { time = 18, value = -3.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = -0.26 }, { time = 9, value = -0.28 }, { time = 18, value = -0.26 }, }, scale_X = { { time = 0, value = 0.8 }, { time = 9, value = 0.8 }, { time = 18, value = 0.8 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "AVANT_BRAS_GAUCHE", keyframes = { location_X = { { time = 0, value = 6.97 }, { time = 9, value = 9.27 }, { time = 18, value = 6.97 }, }, location_Y = { { time = 0, value = 31.01 }, { time = 9, value = 30.95 }, { time = 18, value = 31.01 }, }, location_Z = { { time = 0, value = -3.0 }, { time = 9, value = -3.0 }, { time = 18, value = -3.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.26 }, { time = 9, value = 0.17 }, { time = 18, value = 0.26 }, }, scale_X = { { time = 0, value = 0.8 }, { time = 9, value = 0.8 }, { time = 18, value = 0.8 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "BRAS_DROIT", keyframes = { location_X = { { time = 0, value = -3.95 }, { time = 9, value = -3.44 }, { time = 18, value = -3.95 }, }, location_Y = { { time = 0, value = -15.88 }, { time = 9, value = -16.26 }, { time = 18, value = -15.88 }, }, location_Z = { { time = 0, value = -1.0 }, { time = 9, value = -1.0 }, { time = 18, value = -1.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = -0.19 }, { time = 9, value = -0.29 }, { time = 18, value = -0.19 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "BRAS_GAUCHE", keyframes = { location_X = { { time = 0, value = 0.26 }, { time = 9, value = 1.75 }, { time = 18, value = 0.26 }, }, location_Y = { { time = 0, value = 13.4 }, { time = 9, value = 13.84 }, { time = 18, value = 13.4 }, }, location_Z = { { time = 0, value = -1.0 }, { time = 9, value = -1.0 }, { time = 18, value = -1.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = -0.16 }, { time = 9, value = -0.1 }, { time = 18, value = -0.16 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "CAPUCHE", keyframes = { location_X = { { time = 0, value = 8.41 } }, location_Y = { { time = 0, value = 0.0 } }, location_Z = { { time = 0, value = 1.0 } }, rotation_euler_X = { { time = 0, value = 0.0 } }, rotation_euler_Y = { { time = 0, value = 0.0 } }, rotation_euler_Z = { { time = 0, value = 0.0 } }, scale_X = { { time = 0, value = 1.0 } }, scale_Y = { { time = 0, value = 1.0 } }, scale_Z = { { time = 0, value = 1.0 } } } },
        { name = "DOS", keyframes = { location_X = { { time = 0, value = -1.0 } }, location_Y = { { time = 0, value = 0.0 } }, location_Z = { { time = 0, value = 0.0 } }, rotation_euler_X = { { time = 0, value = 0.0 } }, rotation_euler_Y = { { time = 0, value = 0.0 } }, rotation_euler_Z = { { time = 0, value = 0.0 } }, scale_X = { { time = 0, value = 1.0 } }, scale_Y = { { time = 0, value = 1.0 } }, scale_Z = { { time = 0, value = 1.0 } } } },
        { name = "GRENADES", keyframes = { location_X = { { time = 0, value = -5.35 }, { time = 9, value = -3.5 }, { time = 18, value = -5.35 }, }, location_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, location_Z = { { time = 0, value = -4.0 }, { time = 9, value = -4.0 }, { time = 18, value = -4.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "JAMBE_DROITE_BACK_SUP", keyframes = { location_X = { { time = 0, value = -13.65 }, { time = 9, value = 2.81 }, { time = 18, value = -13.65 }, }, location_Y = { { time = 0, value = -10.7 }, { time = 9, value = -10.7 }, { time = 18, value = -10.7 }, }, location_Z = { { time = 0, value = -5.0 }, { time = 9, value = -5.0 }, { time = 18, value = -5.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 0.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "JAMBE_DROITE_FRONT", keyframes = { location_X = { { time = 0, value = 1.84 }, { time = 9, value = 17.46 }, { time = 18, value = 1.84 }, }, location_Y = { { time = 0, value = -9.09 }, { time = 9, value = -10.2 }, { time = 18, value = -9.09 }, }, location_Z = { { time = 0, value = -5.0 }, { time = 9, value = -5.0 }, { time = 18, value = -5.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 0.0 }, { time = 9, value = 1.0 }, { time = 18, value = 0.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "JAMBE_GAUCHE_BACK_SUP", keyframes = { location_X = { { time = 0, value = -0.24 }, { time = 9, value = -12.54 }, { time = 18, value = -0.24 }, }, location_Y = { { time = 0, value = 6.46 }, { time = 9, value = 6.09 }, { time = 18, value = 6.46 }, }, location_Z = { { time = 0, value = -5.0 }, { time = 9, value = -5.0 }, { time = 18, value = -5.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 0.0 }, { time = 9, value = 1.0 }, { time = 18, value = 0.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "JAMBE_GAUCHE_FRONT", keyframes = { location_X = { { time = 0, value = 14.87 }, { time = 9, value = 2.7 }, { time = 18, value = 14.87 }, }, location_Y = { { time = 0, value = 8.09 }, { time = 9, value = 8.09 }, { time = 18, value = 8.09 }, }, location_Z = { { time = 0, value = -5.0 }, { time = 9, value = -5.0 }, { time = 18, value = -5.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 0.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "MAIN_DROITE_FERMEE", keyframes = { location_X = { { time = 0, value = 16.17 }, { time = 9, value = 10.44 }, { time = 18, value = 16.17 }, }, location_Y = { { time = 0, value = -35.67 }, { time = 9, value = -37.79 }, { time = 18, value = -35.67 }, }, location_Z = { { time = 0, value = -0.0 }, { time = 9, value = -0.0 }, { time = 18, value = -0.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = -0.23 }, { time = 9, value = -0.3 }, { time = 18, value = -0.23 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "MAIN_GAUCHE_FERMEE", keyframes = { location_X = { { time = 0, value = 18.11 }, { time = 9, value = 19.77 }, { time = 18, value = 18.11 }, }, location_Y = { { time = 0, value = 33.56 }, { time = 9, value = 31.68 }, { time = 18, value = 33.56 }, }, location_Z = { { time = 0, value = -0.0 }, { time = 9, value = -0.0 }, { time = 18, value = -0.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.26 }, { time = 9, value = 0.06 }, { time = 18, value = 0.26 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "PIED_DROIT_BACK", keyframes = { location_X = { { time = 0, value = -30.22 }, { time = 9, value = 34.58 }, { time = 18, value = -30.22 }, }, location_Y = { { time = 0, value = -10.94 }, { time = 9, value = -8.34 }, { time = 18, value = -10.94 }, }, location_Z = { { time = 0, value = -6.0 }, { time = 9, value = -6.0 }, { time = 18, value = -6.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "PIED_DROIT_BACK_SEMELLE", keyframes = { location_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, location_Y = { { time = 0, value = -9.77 }, { time = 9, value = -9.77 }, { time = 18, value = -9.77 }, }, location_Z = { { time = 0, value = -6.0 }, { time = 9, value = -6.0 }, { time = 18, value = -6.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "PIED_GAUCHE_BACK", keyframes = { location_X = { { time = 0, value = 31.99 }, { time = 9, value = -26.88 }, { time = 18, value = 31.99 }, }, location_Y = { { time = 0, value = 6.36 }, { time = 9, value = 5.99 }, { time = 18, value = 6.36 }, }, location_Z = { { time = 0, value = -6.0 }, { time = 9, value = -6.0 }, { time = 18, value = -6.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
        { name = "PIED_GAUCHE_BACK_SEMELLE", keyframes = { location_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, location_Y = { { time = 0, value = 4.79 }, { time = 9, value = 4.79 }, { time = 18, value = 4.79 }, }, location_Z = { { time = 0, value = -6.0 }, { time = 9, value = -6.0 }, { time = 18, value = -6.0 }, }, rotation_euler_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Y = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, rotation_euler_Z = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_X = { { time = 0, value = 0.0 }, { time = 9, value = 0.0 }, { time = 18, value = 0.0 }, }, scale_Y = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, scale_Z = { { time = 0, value = 1.0 }, { time = 9, value = 1.0 }, { time = 18, value = 1.0 }, }, } },
    },
And finally I retrieve it and update the positions, rotations and scales according to the current time, and blending from current states when starting a new animation. I'm also using the location_z value to control the render order for all player parts.

Re: STEAL - top down stealth game

Posted: Mon Nov 10, 2014 11:58 pm
by undef
Nice gif!
But the code your plugin creates is not very readable... A couple of new lines and proper indentation would look nicer.

Anyway, keep it up! :)

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 10:36 am
by Fenrir
Hi everyone,

I'm back with gifs showing currently available gadgets. We didn't worked on cool effects yet for them, so don't hesitate if you have some ideas for us (specially for the blink). So here it is:

Sound lure:

Image

EMP grenade:

Image

Invisibility:

Image

Blink (short distance teleportation):

Image

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 10:52 am
by kikito
Cool. A couple comments:
  • When invisible, I would expect the guards to notice you if you touch them, or are as near to them as you are on the invisibility gif
  • The blink gif feels too instantaneous. I suggest replacing it with a "very fast movement" (like 0.3 seconds) so that it is "felt" by the camera and the player. I would also put some particles and/or some kind of "effect" to display the distance travelled for a short period of time after the blink.

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 12:00 pm
by Fenrir
Thanks for the comments!

About the invisibility and collisions with guards (or other players in multiplayer), it is quite a big topic I haven't tackled yet, but definitely the idea will to be noticed when touching someone else.

About the blink, we'll probably make a fast movement instead of the current teleportation as you suggest, but one concern we have is for the multiplayer mode, we don't want other players to know where you teleported by seeing particles showing your path, so we'll probably go for an effect in the leaving location and a new one where you teleported, and probably some kind of blur during the movement for the player who is teleporting.

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 12:22 pm
by kikito
we'll probably make a fast movement instead of the current teleportation as you suggest, but one concern we have is for the multiplayer mode, we don't want other players to know where you teleported by seeing particles showing your path
You could make your teleport particles/effect only visible to the player who teleported - what I meant was that the player will need more feedback about what he's done, exactly.

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 12:24 pm
by micha
Fenrir wrote: About the blink, we'll probably make a fast movement instead of the current teleportation as you suggest, but one concern we have is for the multiplayer mode, we don't want other players to know where you teleported by seeing particles showing your path, so we'll probably go for an effect in the leaving location and a new one where you teleported, and probably some kind of blur during the movement for the player who is teleporting.
It will probably be enough if you teleport the player but move the camera a bit slower. That way, the traveled distance can be seen by the player.

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 12:47 pm
by Fenrir
kikito wrote:You could make your teleport particles/effect only visible to the player who teleported - what I meant was that the player will need more feedback about what he's done, exactly.
Yes I agree, it really lacks of feedback, and yeah maybe doing a specific effect for the one teleporting is the route to go, but still need to find a proper effect...
micha wrote: It will probably be enough if you teleport the player but move the camera a bit slower. That way, the traveled distance can be seen by the player.
Well currently I need the player to be at the center of the screen to properly compute its field of view, so it may be weird if the FOV is moving with the camera and not with the player... :/

Re: STEAL - top down stealth game

Posted: Tue Jan 13, 2015 3:27 pm
by undef
Fenrir wrote:
kikito wrote:You could make your teleport particles/effect only visible to the player who teleported - what I meant was that the player will need more feedback about what he's done, exactly.
Yes I agree, it really lacks of feedback, and yeah maybe doing a specific effect for the one teleporting is the route to go, but still need to find a proper effect...
micha wrote: It will probably be enough if you teleport the player but move the camera a bit slower. That way, the traveled distance can be seen by the player.
Well currently I need the player to be at the center of the screen to properly compute its field of view, so it may be weird if the FOV is moving with the camera and not with the player... :/
You could lerp the position of the player relatively quickly and not draw him during the transition.
I wouldn't recommend a linear interpolation though, but something that eases in and out more smoothly, like a sine or a smoothstep.

Anyway, keep up the good work, I love stealth games :)