2D TEntity module

BlitzMax Forums/BlitzMax Programming/2D TEntity module

Cocopino(Posted 2017) [#1]
Hi, does anyone know of an entity class for 2d Blitzmax that behaves similarly to minib3d?
Pseudo code:

Type TEntity

	Field position:TVector2
	Field rotation:Float
	Field image:TImage

	Method PointAt(v:TVector2)
		'code missing
	End Method

	Method Move(speed:Float)
		'code missing
	End Method
	
	Method Turn(speed:Float)
		'code missing
	End Method

	Method Draw()
		SetRotation(rotation)
		DrawImage(image, position.x, position.y)
		SetRotation(0)
	End Method
	
EndType

Type TVector2

	Field x:Float
	Field y:Float
	
End Type


Thanks!


RustyKristi(Posted 2017) [#2]
I have a good memory so you're in luck Cocopino.. ;-)

Try Weibow's Game2D engine..

https://github.com/wiebow/game2d.mod/blob/master/source/entity/TEntity.bmx

maybe that's close to what you're looking for.


Cocopino(Posted 2017) [#3]
That looks nice, thanks!
It even has a "shake camera" feature :)


mingw(Posted 2017) [#4]
WIP TEntity.bmx

SuperStrict

Module engine.core

Import max2d.max2d
Import engine.Script
'Import brl.maxlua

Import BRL.Stream

Import BRL.PNGLoader
Import BRL.JPGLoader
Import BRL.TGALoader
Import BRL.BMPLoader

Import BRL.TextStream

Import DATA.THash
Import DATA.TData

Import BAH.ChipMunk

'Import BAH.BOX2D

Include "include/TAspect.bmx"
Include "include/TListData.bmx"
Include "include/etc.bmx"
 
Private

Global cpWorld:CPSpace = New CPSpace

Global world:TList = New TList, add@ = True

Global oldPick:TEntity, newPick:TEntity

Global post:TList = New TList

Function Linear#(a#, b#, m#)
	Return a + (b - a) * m
End Function

Public

Type TEntity Extends TLuaObject Final
	'id
	Field name$
	
	'active state
	Field _hide@
	
	'transform
	Field _pos:Vec2 = New Vec2
	Field _scl:Vec2 = Vec2.Create(1.0, 1.0)
	Field _ang#
	
	'tween
	'Field _tween# Ptr = MemAlloc(20)
	Field _oldx#			{no_save}
	Field _oldy#			{no_save}
	Field _oldsx# = 1.0		{no_save}
	Field _oldsy# = 1.0		{no_save}
	Field _oldang# = _ang	{no_save}
	
	'script
	Field _path$
	
	'hierarhy
	Field _parent:TEntity 			{ no_save no_copy hide }
	Field _link:TLink = New TLink { no_save no_copy }
	Field _childs:TList = New TList	{ no_copy }
	
	'aspects
	Field _aspects:THash = New THash
	
	Method SendMessage:Object(message:Object, context:Object)
		Local m$ = String(message)
		
		Select m.ToLower()
		Case "angle", "rotate"
		
		Case "turns"
			If context = "set"
				DebugLog "TURN?"
				Turn(Float(String(lua_result)))
			End If
			Return FloatTypeId
		Case "hide"
			If context = "get"
				lua_result = String(GetHide())
			Else
				DebugLog "SetHide = "+String(lua_result)
				_hide = String(lua_result).ToInt()
				DebugLog "Hide field: " + String(_hide)
			End If
			Return BoolTypeId
		Case "name"
			If context = "get"
			
			Else
			
			End If
		Case "path"
			If context = "get"
				lua_result = _path
			Else
				'DebugLog "setter!"
				_path = String(lua_result)
			End If
			Return StringTypeId
		End Select
		
		If context = "get"
			Local obj:Object = _aspects.ValueForKey(m)
			If obj
				lua_result = obj
				Return ObjectTypeId
			End If
		Else
		
		End If
		
	End Method
	
	Method New()
		name = "Entity_" + Super.ToString()
		If add = True Then _link = world.AddLast(Self)
	End Method
	
	Method Free()
		Invoke("Free")
		For Local a:TAspect = EachIn _aspects.Values()
			a.Free(Self)
		Next
		For Local child:TEntity = EachIn _childs
			child.Free()
		Next
		_link.Remove()
		_link = Null
		_parent = Null
		'MemFree(_tween)
		'If _parent
		'	_parent._childs.Remove(Self)
		'Else
		'	world.Remove(Self)
		'End If
	End Method
	
	Method SetHide(hide@)
		_hide = hide
	End Method
	
	Method GetHide@()
		If _hide Then Return True
		
		Local e:TEntity = _parent
		While e
			If e._hide Then Return True
			e = e._parent
		Wend
		
		Return False
	End Method
	
	Method Copy:TEntity(parent:TEntity = Null)
	
	End Method
	
	Method SetParent(parent:TEntity = Null, isGlobal@ = True)
		If _parent = parent Then Return
		
		Local gp:Vec2 = GetPosition(True)
		Local gr:Float = GetRotation(True)
		Local gs:Vec2 = GetScale(True)
		
		_link.Remove()
		_link = Null
		
		_parent = parent
		
		If _parent = Null
			_pos.Set(gp.x, gp.y)
			_ang = gr
			_scl.Set(gs.x, gs.y)
			_link = world.AddLast(Self)
			Return
		End If
		
		_link = _parent._childs.AddLast(Self)
		
		If isGlobal = True
			SetPosition(gp, True)
			SetRotation(gr, True)
			SetScale(gs, True)
		Else
			SetPosition(_pos, False)
			SetRotation(_ang, False)
			SetScale(_scl, False)
		End If
	End Method
	
	Method GetParent:TEntity()
		Return _parent
	End Method
	
	Method FindChild:TEntity(name$, isGlobal@ = False)
		For Local child:TEntity = EachIn _childs
			If child.name = name Then Return child
			If Not isGlobal Then Continue
			child = child.FindChild(name, True)
			If child Then Return child
		Next
	End Method
	
	Method CountChildren%()
		Return _childs.Count()
	End Method
	
	Method GetChild:TEntity(index%)
		Return TEntity(_childs.ValueAtIndex(index))
	End Method
	
	Method SetPosition(pos:Vec2, isGlobal@ = False)
		If isGlobal And _parent
			_pos.Sub(_parent.GetPosition(True))
			_pos.Div(_parent.GetScale(True))
			_pos.Rotate(Vec2.ForAngle(_parent.GetRotation(True)))
		Else
			_pos.Set(pos.x, pos.y)
		End If
		_oldx = _pos.x
		_oldy = _pos.y
	End Method
	
	Method GetPosition:Vec2(isGlobal@ = False)
		If isGlobal And _parent
			
			Local pos:Vec2 = New Vec2
			Local ent:TEntity = Self
			
			While ent
				pos.Add(ent._pos)
				If ent._parent
					pos.Mul(ent._parent._scl)
					pos.Rotate(Vec2.ForAngle(ent._parent._ang))
				End If
				ent = ent._parent
			Wend
			
			Return pos
		End If
		Return _pos.Copy()
	End Method
	
	Method SetRotation(angle#, isGlobal@ = False)
		If isGlobal And _parent
			_ang = (angle - _parent.GetRotation(True))' Mod 360
		Else
			_ang = angle' Mod 360
		End If
		_oldang = _ang
	End Method
	
	Method GetRotation#(isGlobal@ = False)
		If isGlobal And _parent
			
			Local ent:TEntity = _parent
			Local ang# = _ang
			
			While ent
				ang:+ent._ang
				ent = ent._parent
			Wend
			
			Return ang' Mod 360
		End If
		Return _ang' Mod 360
	End Method
	
	Method SetScale(scl:Vec2, isGlobal@ = False)
		If isGlobal And _parent
			_scl = _parent.GetScale(True) '.Div(scl)
			_scl.Div(scl)
		Else
			_scl.Set(scl.x, scl.y)
		End If
		_oldsx = _scl.x
		_oldsy = _scl.y
	End Method
	
	Method GetScale:Vec2(isGlobal@ = False)
		If isGlobal And _parent
			Local ent:TEntity = _parent
			Local scl:Vec2 = _scl.Copy()
			
			While ent
				scl.Mul(ent._scl)
				ent = ent._parent
			Wend
			
			Return scl
		End If
		Return _scl.Copy()
	End Method
	
	Method Distance#(target:TEntity)
		Local v:Vec2 = GetPosition(True)
		v.Sub(target.GetPosition(True))
		Return Abs(v.Length())
	End Method
	
	Method DeltaAngle#(target:TEntity)
	
	End Method
	
	Method Align(v:Vec2, m# = 1.0)
		
	End Method
	
	Method Compare%(with:Object)
		Local ent:TEntity = TEntity(with)
		
	End Method
	
	Method Move(v:Vec2)
		_oldx = _pos.x
		_oldy = _pos.y
		_pos.x:+v.x
		_pos.y:+v.y
	End Method
	
	Method Turn(angle#)
		_oldang = _ang
		_ang:+angle
	End Method
	
	Method Translate(v:Vec2)
		
	End Method
	
	Method Point(target:TEntity)
		
	End Method
	
	Method ObjectEnumerator:TListEnum()
		Return _childs.ObjectEnumerator()
	End Method
End Type

Function CreateEntity:TEntity(parent:TEntity = Null)
	Local entity:TEntity = New TEntity
	If parent Then entity.SetParent(parent)
	Return entity
End Function

Function LoadEntity:TEntity(url:Object, parent:TEntity = Null)
	add = False
	Local obj:Object = LoadObject(url)
	add = True
	
	'manager.Load(Null)
	
	Local entity:TEntity = TEntity(obj)
	
	If entity
		
		Load(entity)
		entity.SetParent(parent)
		
	Else
	
		Local list:TList = TList(obj)
		
		For entity:TEntity = EachIn list
			
			Load(entity)
			entity.SetParent(parent)
			
		Next
		
		entity = Null
		
	End If
	
	Return entity
	
	Function Load(entity:TEntity)
		If entity._path <> ""
			entity.SetClass(TLuaClass(Manager.Get(entity._path)))
		End If
		For Local a:TAspect = EachIn entity._aspects.Values()
			a.Load(entity)
		Next
		For Local child:TEntity = EachIn entity._childs
			Load(child)
		Next
		entity.Invoke("Load")
	End Function
End Function

Function SaveEntity(url:Object, entity:TEntity = Null)	
	If entity
		SaveObject(entity, url)
	Else
		SaveObject(world, url)
	End If
End Function

Function ClearWorld()
	For Local entity:TEntity = EachIn world
		entity.Free()
	Next
	world = New TList
End Function

Function UpdateWorld(capture@ = True)

	mouse.x = MouseX()
	mouse.y = MouseY()
	mouse.z = MouseZ()
	
	mouse.xspeed = MouseXSpeed()
	mouse.yspeed = MouseYSpeed()
	mouse.zspeed = MouseZSpeed()
	
	If newPick
		
		If newPick <> oldPick
			If oldPick Then oldPick.Invoke("Pick", ["-1"])
			newPick.Invoke("Pick", ["1"])
		Else
			newPick.Invoke("Pick", ["0"])
		End If
		
	End If
	oldPick = newPick
	
	cpWorld.DoStep(1.0 / 60.0)
	
	Local entity:TEntity
	
	post = New TList
	For entity = EachIn world
		UpdateEntity(entity, capture)
	Next
	
	For entity = EachIn post
		entity.Invoke("PostLoop", [String(capture)])
	Next
	
	Function UpdateEntity(entity:TEntity, capture@)
		If entity._hide Then Return
		
		If capture
			entity._oldx = entity._pos.x
			entity._oldy = entity._pos.y
			entity._oldsx = entity._scl.x
			entity._oldsy = entity._scl.y
			entity._oldang = entity._ang
		End If
		
		For Local aspect:TAspect = EachIn entity._aspects.Values()
			aspect.Loop(entity, capture)
		Next
		
		entity.Invoke("Loop", [String(capture)])
		
		For Local child:TEntity = EachIn entity._childs
			UpdateEntity(child, capture)
		Next
		
	End Function
End Function

Function RenderWorld(tween# = 1.0)
	
	Cls(black)
	
	TImage.picked = Null
	newPick = Null
	post = New TList
	
	Local entity:TEntity
	
	Matrix.Push()
	For entity = EachIn world
		Matrix.Load()
		RenderEntity(entity, tween)
	Next
	
	For entity = EachIn post
		For Local aspect:TPostDraw = EachIn entity._aspects.Values()
			Matrix.Load(aspect.Matrix)
			Exit
		Next
		entity.Invoke("PostDraw", [String(tween)])
	Next
	Matrix.Pop()
	
	Function RenderEntity(entity:TEntity, tween#)
		If entity._hide Then Return
		
		Matrix.Translate(Linear(entity._oldx, entity._pos.x, tween), ..
		Linear(entity._oldy, entity._pos.y, tween))
		Matrix.Scale(Linear(entity._oldsx, entity._scl.x, tween), ..
		Linear(entity._oldsy, entity._scl.y, tween))
		Matrix.Rotate(Linear(entity._oldang, entity._ang, tween))
		
		entity.Invoke("Draw", [String(tween)])
		
		For Local aspect:TAspect = EachIn entity._aspects.Values()
			aspect.Draw(entity, tween)
		Next
		
		For Local child:TEntity = EachIn entity._childs
			Matrix.Push()
			RenderEntity(child, tween)
			Matrix.Pop()
		Next
	End Function
End Function

Function CountChildren%(entity:TEntity = Null)
	If entity
		Return entity._childs.Count()
	Else
		Return world.Count()
	End If
End Function

Function GetChild:TEntity(index%, entity:TEntity = Null)
	If entity
		Return TEntity(entity._childs.ValueAtIndex(index))
	Else	
		Return TEntity(world.ValueAtIndex(index))
	End If
End Function

Function FindChild:TEntity(name$, entity:TEntity = Null, isGlobal@ = False)
	If entity
		Return entity.FindChild(name, isGlobal)
	Else
		For entity = EachIn world
			
		Next
	End If
End Function


WIP Aspects.bmx:

Public

Type TAspect Abstract
	Method Load(entity:TEntity) 
	End Method
	Method Free(entity:TEntity) 
	End Method
	Method Loop(entity:TEntity, capture@) 
	End Method
	Method Draw(entity:TEntity, tween#) 
	End Method
End Type

Type TBody Extends TAspect
	'Field body:CPBody
	'Field shape:CPShape[]
	'Field shape:tlBox
	
	Method Loop(entity:TEntity, capture@)
		
	End Method
End Type

Type TMouseAspect Extends TAspect
	Method Loop(entity:TEntity, capture@)
		entity.SetPosition(Vec2.Create(MouseX(), MouseY()), True)
	End Method
End Type

Type TPostLoop Extends TAspect { single }
	Method Loop(entity:TEntity, capture@)
		post.AddLast(entity)
	End Method
End Type

Type TPostDraw Extends TAspect { single }
	Field Matrix#[6] { no_edit }
	
	Method Draw(entity:TEntity, tween#)
		MemCopy(Matrix, Varptr .Matrix.ix, 24)
		post.AddLast(entity)
	End Method
End Type

Type TLineCompare Extends TAspect
	Field xy#[]
End Type

Type TChildSorter Extends TAspect
	Method Loop(entity:TEntity, capture@)
		Local array:Object[] = entity._childs.ToArray()
		qsort(array)
		entity._childs = TList.FromArray(array)
	End Method
	
	Function qsort(array:Object[])
		
	End Function
End Type

Type TAnimator Extends TAspect
	Field keys#[]
	Field AnimSpeed#
	Field AnimTime#
	Field AnimMode@
	Field AnimFunc@
	
	Method Loop(entity:TEntity, c@)
		
	End Method
End Type

Type TSoundStreamed Extends TAspect
	Field path$
	
	
End Type

Type TSoundAspect Extends TAspect
	'Field sound:TSound 	{ no_save }
	Field path$			{ manager }
	Field flags%
	
	Method Play()
	'	PlaySound(sound)
	End Method
	
	Method Load(entity:TEntity)
	
	End Method
	
	
End Type

Type TImageAspect Extends TAspect
	Field image:TImage 	{ no_save }
	Field path$			{ manager }
	Field flags% = -1
	Field blend@ = ALPHABLEND
	Field Color#[] = white[..]
	Field pick@ = False
	
	Method Load(entity:TEntity)
		image = TImage.Load(path, flags)
		'image = TImage.Load(Manager.Get(path), flags)
	End Method
	
	Method Draw(entity:TEntity, tween#)
		SetBlend(blend)
		SetColor(Color)
		If pick Then image.pick = entity
		image.Draw()
		'newPick = TImage.picked
		If TImage.picked = entity
			'entity.Invoke("Pick")
			newPick = entity
		End If
		image.pick = Null
	End Method
End Type

Type TParticleAspect Extends TImageAspect
	
End Type

Type TTextAspect Extends TImageAspect
	Field Text$
	Field charKerning# = 0
	Field lineKerning# = 0
	
	Method Draw(entity:TEntity, tween#)
		SetBlend(blend)
		SetColor(Color)
		SetFont(image, charKerning, lineKerning)
		If pick Then image.pick = entity
		DrawText(Text)
		If TImage.picked = entity
			'entity.Invoke("Pick")
			newPick = entity
		End If
		image.pick = Null
	End Method
End Type