Main Menu

Shoutbox

Dabzy

2025-10-17, 17:36:22
Well, just have to keep an eyes, even though they said they sorted it like

Jackdaw

2025-10-17, 17:22:26
When I saw it. It was in the very early hours when normal people are fast asleep. Lasted for around half an hour before back to normal.

Dabzy

2025-10-17, 15:02:27
First time I seen it, having me cuppa at work and had a browse... Saw it and I was like "WTF is that!?!", so got on the blower!

Baggey

2025-10-17, 11:22:52
Oh goody. Was getting withdraw symptons!

Jackdaw

2025-10-17, 10:38:44
I saw that the other week, and forgot to query it. Redirects to sedo.parking.

Dabzy

2025-10-17, 10:33:24
Seems we lost the site for a bit there, sorted, basically htaccess went a bit screwy and it was diverting to one of them stupid default search pages... Been on to ionos the host provider and they fixered it! \o/ Had meself a bit of a sweat on there!  ;D

Jackdaw

2025-10-16, 20:00:40
Going to have to try bourbon and beans. That should be an explosive combination.

Baggey

2025-10-16, 13:15:42
I sometimes mix a chicken vindaloo and a Tika Masala together. Awesome  :P

Dabzy

2025-10-16, 05:49:34
And doing the act was the realisation I went for an indian when out... 20mins I was in the thunderbox waiting for me back chaff to cool down!

Dabzy

2025-10-16, 05:48:11
When I was on my "Year On The Blur", aka drinking after getting divorced, I was minging one night, couldnt remember getting home. Anyway, next day, went to work, and needed a poo...

Members
Stats
  • Total Posts: 1,816
  • Total Topics: 226
  • Online today: 29
  • Online ever: 232 (Oct 08, 2025, 09:18 AM)
Users Online
  • Users: 0
  • Guests: 5
  • Total: 5
Welcome to SyntaxBoom. Please login or sign up.

Recent

Game Object Movement and Animation States

Started by Jackdaw, Jul 19, 2025, 11:22 AM

Previous topic - Next topic

Jackdaw

Not often that I will post something, but here's a little something based from a chapter from the book 'Game Programming Patterns'. It's very primitive, and there are no comments, as the code is quite large.

Demo video


There's also a Cerberus-X version.

You will need to download the warpsara-nohelmet-anim-sheet-alpha_1.png file from https://opengameart.org/content/space-sara and remain it heroine.png

Code  blitzmax Select
Enum EACTIONS
	NONE = 0,
	MOVE_LEFT = 1,
	MOVE_RIGHT,
	JUMP,
	JUMP_LEFT,
	JUMP_RIGHT,
	DUCK
End Enum

Const FLAG_FACE:UInt = $01
Const FLAG_RUNNING:UInt = $02

Type TFrame
	Field _srcX:Int
	Field _srcY:Int
	Field _srcW:Int
	Field _srcH:Int
End Type

Type TAnimation
	Field _frames:TFrame[]
	Field _totalFrames:Int
	Field _frameRate:Float

	Method GetFrameRate:Float()
		Return _frameRate
	End Method

	Method GetFrameData:TFrame(currentFrame:Int)
		Return _frames[currentFrame]
	End Method

	Method AddFrames(totalFrames:Int, frameRate:Float, column:Int, row:Int, frameWidth:Int = 48, frameHeight:Int = 48)
		Local cfl:Int = 0
		If _frames = Null
			_frames = New TFrame[totalFrames]
		Else
			cfl = _frames.Length
			_frames = _frames[..(_frames.length + totalFrames)]
		EndIf
		_frameRate = 1000 / frameRate
		_totalFrames = _frames.length
		Local i:Int = cfl, p:Int = column * frameWidth
		While i < totalFrames
			_frames[i] = New TFrame
			_frames[i]._srcX = p
			_frames[i]._srcY = row * frameHeight
			_frames[i]._srcW = frameWidth
			_frames[i]._srcH = frameHeight
			i :+ 1
			p:+frameWidth
		Wend
	End Method	
End Type

Type TControllerInput
	Method Update()
		Self._inputState = PRESS_NONE
		If KeyDown(KEY_S) _inputState :| PRESS_DUCK
		If KeyDown(KEY_W) _inputState :| PRESS_JUMP
		If KeyDown(KEY_A) _inputState :| PRESS_LEFT
		If KeyDown(KEY_D) _inputState :| PRESS_RIGHT
		If KeyDown(KEY_SPACE) _inputState :| PRESS_FIRE
	End Method

	Method GetAction:EACTIONS()
		If _inputState & PRESS_JUMP
			If _inputState & PRESS_LEFT
				Return EACTIONS.JUMP_LEFT
			Elseif _inputState & PRESS_RIGHT
				Return EACTIONS.JUMP_RIGHT
			Else
				Return EACTIONS.JUMP
			Endif
		ElseIf _inputState & PRESS_DUCK
			Return EACTIONS.DUCK
		ElseIf _inputState & PRESS_LEFT
			Return EACTIONS.MOVE_LEFT
		ElseIf _inputState & PRESS_RIGHT
			Return EACTIONS.MOVE_RIGHT
		EndIf

		Return EACTIONS.NONE
	End Method
	
	Const PRESS_NONE:Int = $00
	Const PRESS_LEFT:Int = $01
	Const PRESS_RIGHT:Int = $02
	Const PRESS_JUMP:Int = $04
	Const PRESS_DUCK:Int = $08
	Const PRESS_FIRE:Int = $10
	Field _inputState:Byte
End Type

Type THeroine
	Method New()
		_atlas = LoadImage("heroine.png")
		_states = THeroineState.CreateStates()
		SetState(_states._idle1)
		_xSpeed = 0
		_ySpeed = 0
		_flags:|FLAG_FACE
	End Method

	Method HandleInput(action:EACTIONS)
		_state.HandleInput(Self, action)
	End Method

	Method Update()
		_state.Update(Self)
	End Method

	Method Render()
		Local frame:TFrame = _state.GetCurrentFrameData(Self)
		If _flags & FLAG_FACE
			DrawSubImageRect(_atlas, _xPos, _yPos, frame._srcW, frame._srcH, frame._srcX, frame._srcY, frame._srcW, frame._srcH, frame._srcW / 2, frame._srcH / 2, 0)
		ElseIf _flags & FLAG_FACE = 0
			DrawSubImageRect(_atlas, _xPos, _yPos, -frame._srcW, frame._srcH, frame._srcX, frame._srcY, frame._srcW, frame._srcH, frame._srcW / 2, frame._srcH / 2, 0)
		EndIf
	End Method

	Method SetState(state:THeroineState)
		_state = state
		_totalFrameCount = state.GetTotalFrameCount()
		_currentFrame = 0
	End Method

	Method TotalFrameCount:Int()
		Return _totalFrameCount
	End Method

	Method FrameCountEnd:Int()
		Return _totalFrameCount - 1
	End Method

	Method GetStateName:String()
		Return _state._strID
	End Method

	Method GetLastFrameTime:Float()
		Return _lastFrameTime
	End Method

	Method SetLastFrameTime(value:Float)
		_lastFrameTime = value
	End Method

	Method FaceLeft()
		_flags:&~FLAG_FACE
		_currentFrame = 0
		_xSpeed = -8.0
	End Method

	Method FaceRight()
		_flags:|FLAG_FACE
		_currentFrame = 0
		_xSpeed = 8.0
	End Method

	Method EnableRunning()
		_flags:|FLAG_RUNNING
	End Method

	Method DisableRunning()
		_flags:&~FLAG_RUNNING
	End Method

	Global _xPos:Float
	Global _yPos:Float
	Global _xSpeed:Float
	Global _ySpeed:Float
	Global _flags:UInt
	Global _currentFrame:Int
	Field _atlas:TImage
	Field _states:THeroineState
	Field _state:THeroineState
	Field _totalFrameCount:Int
	Field _lastFrameTime:Float
End Type

Type THeroineState
	Field _strID:String
	Field _animationSequence:TAnimation
	Field _idle1:THeroineIdleState
	Field _startRun:THeroineStartRunState
	Field _startRunJump:THeroineStartRunJumpState
	Field _runLoop:THeroineRunLoopState
	Field _stopRun:THeroineStopRunState
	Field _crouch:THeroineCrouchState
	Field _stand:THeroineStandUpState
	Field _startJump:THeroineStandJumpState
	Field _jumpLoop:THeroineJumpLoopState
	Field _stopJump:THeroineStopJumpState

	Method HandleInput(heroine:THeroine, action:EACTIONS)
	End Method
	
	Method Update(heroine:THeroine)
		Local tick:Float = MilliSecs()
		Local elapsed_time:Float = tick - heroine.GetLastFrameTime()
		
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame < heroine.FrameCountEnd()
				 heroine._currentFrame = heroine._currentFrame + 1
			Else
				heroine._currentFrame = 0
			EndIf
			Motion(heroine)
		Endif
	End Method
	
	Method Motion(heroine:THeroine)
		If heroine._xPos < 24 heroine._xPos = 24
		If heroine._xPos > GraphicsWidth() - 25 heroine._xPos = GraphicsWidth() - 25
	End Method

	Method GetCurrentFrameData:TFrame(heroine:THeroine)
		Return heroine._state._animationSequence.GetFrameData(heroine._currentFrame)
	End Method

	Method GetTotalFrameCount:Int()
		Return _animationSequence._totalFrames
	End Method

	Method AddFrames(totalFrames:Int, frameRate:Float, column:Int, row:Int, frameWidth:Int = 48, frameHeight:Int = 48)
		If _animationSequence = Null _animationSequence = New TAnimation()
		_animationSequence.AddFrames(totalFrames, frameRate, column, row, frameWidth, frameHeight)
	End Method

	Function CreateStates:THeroineState()
		Local state:THeroineState = New THeroineState()
		state._idle1 = New THeroineIdleState()
		state._crouch = New THeroineCrouchState()
		state._stand = New THeroineStandUpState()
		state._startRun = New THeroineStartRunState()
		state._runLoop = New THeroineRunLoopState()
		state._stopRun = New THeroineStopRunState()
		state._startJump = New THeroineStandJumpState()
		state._jumpLoop = New THeroineJumpLoopState()
		state._stopJump = New THeroineStopJumpState()
		state._startRunJump = New THeroineStartRunJumpState()
		Return state
	End Function
End Type

Type THeroineCrouchState Extends THeroineState
	Method New()
		_strID = "CROUCH DOWN STATE"
		AddFrames(5, 16.5, 0, 13)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
		If Not (action = EACTIONS.DUCK)
			If heroine._state = heroine._states._crouch heroine.SetState(heroine._states._stand)
		EndIf
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Int = MilliSecs()
		Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame < heroine.FrameCountEnd() heroine._currentFrame = heroine._currentFrame + 1
		Endif
	End Method
End Type

Type THeroineStandUpState Extends THeroineState
	Method New()
		_strID = "STANB UP STATE"
		AddFrames(4, 16.5, 0, 14)
	End Method

	Method HandleInput(heroine:THeroine, input:EACTIONS) Override
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Int = MilliSecs()
		Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame < heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
			Else
				heroine.SetState(heroine._states._idle1)
			EndIf
		EndIf
	End Method
End Type

Type THeroineIdleState Extends THeroineState
	Method New()
		_strID = "IDLE STATE ONE"
		AddFrames(4, 4.5, 0, 1)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
		Select action
			Case EACTIONS.DUCK
				heroine.SetState(heroine._states._crouch)
			Case EACTIONS.MOVE_LEFT
				heroine.SetState(heroine._states._startRun)
				heroine.FaceLeft()
			Case EACTIONS.MOVE_RIGHT
				heroine.SetState(heroine._states._startRun)
				heroine.FaceRight()
			Case EACTIONS.JUMP_LEFT
				heroine.SetState(heroine._states._startRunJump)
				heroine.FaceLeft()
				heroine.EnableRunning()
			Case EACTIONS.JUMP_RIGHT
				heroine.SetState(heroine._states._startRunJump)
				heroine.FaceRight()
				heroine.EnableRunning()
			Case EACTIONS.JUMP
				heroine.SetState(heroine._states._startJump)
		End Select
	End Method

	Method Update(heroine:THeroine) Override
		Super.Update(heroine)
	End Method
End Type

Type THeroineJumpLoopState Extends THeroineState
	Method New()
		_strID = "JUMP LOOP STATE"
		AddFrames(4, 12.5, 0, 8)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
	End Method
	
	Method Update(heroine:THeroine) Override
		Local tick:Int = MilliSecs()
		Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			Motion(heroine)
			If heroine._currentFrame < heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
				DebugLog "FRAME INCREMENT: " + heroine._currentFrame
				If heroine._currentFrame > 2
				 	heroine._ySpeed = 8.0
					DebugLog "FRAME YSPEED TRIGGERED: "+heroine._ySpeed
				EndIf
			Else
				heroine.SetState(heroine._states._stopJump)
				DebugLog "SET FRAME STATE TO STOP JUMP"
			EndIf
		EndIf
	End Method

	Method Motion(heroine:THeroine) Override
		heroine._yPos:+heroine._ySpeed
		If heroine._flags & FLAG_RUNNING
			heroine._xPos:+heroine._xSpeed
			Super.Motion(heroine)
		Endif
	End Method
End Type

Type THeroineStartRunJumpState Extends THeroineState
	Method New()
		_strID = "START RUN JUMP STATE"
		AddFrames(4, 12.5, 0, 6)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Float = MilliSecs()
		Local elapsed_time:Float = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame < heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
				If heroine._currentFrame > 0 heroine._ySpeed = -8.0
			Else
				heroine.SetState(heroine._states._jumpLoop)
				heroine._ySpeed = 0.0
			EndIf
			Motion(heroine)
		EndIf
	End Method

	Method Motion(heroine:THeroine) Override
		heroine._yPos:+heroine._ySpeed
		If heroine._flags & FLAG_RUNNING
			heroine._xPos:+heroine._xSpeed
			Super.Motion(heroine)
		Endif
	End Method
End Type

Type THeroineStopJumpState Extends THeroineState
	Method New()
		_strID = "STOP JUMP STATE"
		AddFrames(2, 12.5, 0, 9)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Float = MilliSecs()
		Local elapsed_time:Float = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			Motion(heroine)
			If heroine._currentFrame < heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
			Else
				heroine.SetState(heroine._states._idle1)
				heroine._ySpeed = 0.0
				heroine.DisableRunning()
			EndIf
		EndIf
	End Method

	Method Motion(heroine:THeroine) Override
		heroine._yPos:+heroine._ySpeed
		If heroine._flags & FLAG_RUNNING
			heroine._xPos:+heroine._xSpeed
			Super.Motion(heroine)
		Endif
	End Method
End Type

Type THeroineStandJumpState Extends THeroineState
	Method New()
		_strID = "START STAND JUMP STATE"
		AddFrames(6, 12.5, 0, 7)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Float = MilliSecs()
		Local elapsed_time:Float = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame < heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
				If heroine._currentFrame > 2 heroine._ySpeed = -8.0
			Else
				heroine.SetState(heroine._states._jumpLoop)
				heroine._ySpeed = 0.0
			EndIf
			Motion(heroine)
		EndIf
	End Method

	Method Motion(heroine:THeroine) Override
		heroine._yPos:+heroine._ySpeed
	End Method
	
End Type

Type THeroineStartRunState Extends THeroineState
	Method New()
		_strID = "START RUN STATE"
		AddFrames(1, 12.5, 0, 3)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
		Select action
			Case EACTIONS.MOVE_LEFT
				If (heroine._flags & FLAG_FACE)
					heroine.FaceLeft()
				Else
					heroine.EnableRunning()
				EndIf
			Case EACTIONS.MOVE_RIGHT
				If (heroine._flags & FLAG_FACE) = 0
					heroine.FaceRight()
				Else
					heroine.EnableRunning()
				EndIf
			Case EACTIONS.JUMP_LEFT
				heroine.SetState(heroine._states._startRunJump)
				heroine.EnableRunning()
			Case EACTIONS.JUMP_RIGHT
				heroine.SetState(heroine._states._startRunJump)
				heroine.EnableRunning()
			Default
				heroine.SetState(heroine._states._stopRun)
				heroine.DisableRunning()
		End Select
	End Method

	Method Update(heroine:THeroine) Override
		Local tick:Int = MilliSecs()
		Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame > heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
			Else
				heroine.SetState(heroine._states._runLoop)
			EndIf
			Motion(heroine)	
		Endif
	End Method

	Method Motion(heroine:THeroine) Override
		If heroine._xPos > 23 And heroine._xPos < GraphicsWidth() - 24 heroine._xPos:+heroine._xSpeed
		Super.Motion(heroine)
	End Method
End Type

Type THeroineRunLoopState Extends THeroineState
	Method New()
		_strID = "RUN STATE"
		AddFrames(10, 12.5, 0, 4)
	End Method

	Method HandleInput(heroine:THeroine, action:EACTIONS) Override
		Select action
			Case EACTIONS.MOVE_LEFT
				If (heroine._flags & FLAG_FACE)
					heroine.FaceLeft()
				Else
					heroine._flags:|FLAG_RUNNING
				EndIf
			Case EACTIONS.MOVE_RIGHT
				If (heroine._flags & FLAG_FACE) = 0
					heroine.FaceRight()
				Else
					heroine._flags:|FLAG_RUNNING
				EndIf
			Case EACTIONS.JUMP_LEFT
				heroine.SetState(heroine._states._startRunJump)
				heroine.FaceLeft()
			Case EACTIONS.JUMP_RIGHT
				heroine.SetState(heroine._states._startRunJump)
				heroine.FaceRight()
			Default
				heroine.SetState(heroine._states._stopRun)
				heroine.DisableRunning()
		End Select
	End Method

	Method Update(heroine:THeroine) Override
		If heroine._flags & FLAG_RUNNING
			Local tick:Int = MilliSecs()
			Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
			If elapsed_time >= _animationSequence.GetFrameRate()
				heroine.SetLastFrameTime(tick)
				If heroine._currentFrame < heroine.FrameCountEnd()
					heroine._currentFrame = heroine._currentFrame + 1
				Else
					heroine._currentFrame = 0
				EndIf
				Motion(heroine)
			EndIf
		EndIf
	End Method

	Method Motion(heroine:THeroine) Override
		If heroine._xPos > 23 And heroine._xPos < GraphicsWidth() - 24 heroine._xPos:+heroine._xSpeed
		Super.Motion(heroine)
	End Method
End Type

Type THeroineStopRunState Extends THeroineState
	 Method New()
		_strID = "STOP RUN STATE"
		AddFrames(3, 12.5, 0, 5)
	 End Method

	 Method HandleInput(heroine:THeroine, action:EACTIONS) Override	
	 End Method

	 Method Update(heroine:THeroine) Override
		Local tick:Int = MilliSecs()
		Local elapsed_time:Int = tick - heroine.GetLastFrameTime()
		If elapsed_time >= _animationSequence.GetFrameRate()
			heroine.SetLastFrameTime(tick)
			If heroine._currentFrame > heroine.FrameCountEnd()
				heroine._currentFrame = heroine._currentFrame + 1
			Else
				heroine.SetState(heroine._states._idle1)
				heroine.DisableRunning()
			EndIf
			Motion(heroine)
		Endif
	 End Method

	 Method Motion(heroine:THeroine) Override
		If heroine._xPos > 23 And heroine._xPos < GraphicsWidth() - 24 heroine._xPos:+heroine._xSpeed
		Super.Motion(heroine)
	End Method
End Type

Type TGameLoop
	Method New(width:Int = 800, height:Int = 600, depth:Int = 0, hertz:Int = 60, flags:Int = GRAPHICS_BACKBUFFER)
		_graphics = Graphics(width, height, depth, hertz, flags, 400, 400)
		player = New THeroine()
		controls = New TControllerInput()	
		player._xPos = GraphicsWidth() / 2
		player._yPos = GraphicsHeight() / 2
		Run()
	End Method

	Method Run()
		While Not KeyDown(KEY_ESCAPE)
			Cls
			DrawRect(0, (GraphicsHeight() / 2) + 24, GraphicsWidth(), 8)
			DrawText("EXAMPLE 001: ANIMATION STATES", 0, 0)
			DrawText("BASED ON STATES CHAPTER FROM: https://gameprogrammingpatterns.com/",0, 12)
			DrawText("MEDIA FROM: https://opengameart.org/content/space-sara", 0, 25)
			DrawText("HEROINE STATE: " + player.GetStateName(), 0, 38)
			DrawText("XPOS: " + player._xPos + " - YPOS: " + player._yPos, 0, 51)
			DrawText("KEYS: W,A,D,S AND ESCAPE TO QUIT", 0, 64)
			controls.Update()
			player.HandleInput(controls.GetAction())
			player.Update()
			player.Render()
			Flip
		Wend
	End Method

	Field _graphics:TGraphics
	Field player:THeroine
	Field controls:TControllerInput
End Type

New TGameLoop()
If you've dug yourself into a hole. Just keep digging. You're bound to come out the other side eventually.

Jackdaw

Okay. If you want to see a better example of a BlitzMax project.
Then you can download the sources from here.

You will still need to download the image file linked above.

It's a shame that BlitzMax doesn't support method properties like Cerberus-X. It would make it a lot easier to work with.
If you've dug yourself into a hole. Just keep digging. You're bound to come out the other side eventually.