Hi all,
It's been a long while since I touched Lua or Love, so this is my first project since then. I wrote a toy component based entity system. It's not done yet, but I figured I should just throw it out in the wild, in an attempt to get early feedback.
Right now, there are just three components to an Entity:
* View - responsible for drawing to the screen and the related transforms
* Movement - responsible for moving the entity, either over time or from player input
* Model - Holds the entity states, such as position, velocity, acceleration, etc.
Currently, it's hardcoded that you use all three, but as I add more component, one would be able to customize it.
It also has a parent-child hierarchy to compose entities (I'm not sure what the formal name is, but I've heard "frames" If anyone knows, lemme know)
The demo shows a magician wearing a helmet, carrying a wand, shield, and the shield has a cow jittering in front of it. The magician has helmet, wand, and shield as children, and the shield has the cow as a child. When you move the character around, with W,A,S,D keys, you can see that the accessories move around with the magician, even though the accessories have a local coordinate system.
The demo displays the vectors for acceleration and velocity. you can also pan and zoom the camera with the arrow keys and the square bracket keys.
I'll be posting updates as I do more.
github: https://github.com/iamwilhelm/hamstercupid
HamsterCupid: A toy component based entity system
HamsterCupid: A toy component based entity system
- Attachments
-
- hamstercupid.love
- (786.37 KiB) Downloaded 468 times
Re: HamsterCupid: A toy component based entity system
cool.
Something very useful.
Something very useful.
Re: HamsterCupid: A toy component based entity system
I've updated hamstercupid to fix some of the motion bugs that I found. The entities can be composed like the following:
The magician is holding a shield, and the shield has a cow. (There are also other options where you can set the scale and rotation of the child entities.)
And now the motions can also be composed:
The motion names aren't permanent yet, but wiggle() moves the cow vertically back and forth. waggle() moves the cow horizontally back and forth. As a result, the cow moves back and forth diagonally. The amplitude of moving back and forth is 50, and it has a period of 1 second.
What if we wanted the cow to orbit the shield in a circle?
We simply make the waggle motion delayed by a phase of 90 deg.
And when you move the magician around, the cow still circles the shield correctly. Whee.
Code: Select all
magician = Entity:new("magician", V:new(400, 200))
magician.view:setImage("resources/rpg/magician.front.gif")
shield = Entity:new("shield", V:new(15, 10))
shield.view:setImage("resources/rpg/adamant.shield.gif")
magician:addChild(shield)
cow = Entity:new("cow", V:new(50, 0))
cow.view:setImage("resources/cow.png")
shield:addChild(cow)
And now the motions can also be composed:
Code: Select all
cow.movement:addMovement(Motion.wiggle(50, 1))
cow.movement:addMovement(Motion.waggle(50, 1))
What if we wanted the cow to orbit the shield in a circle?
Code: Select all
cow.movement:addMovement(Motion.wiggle(50, 1))
cow.movement:addMovement(Motion.waggle(50, 1, math.rad(90))
And when you move the magician around, the cow still circles the shield correctly. Whee.
- Attachments
-
- hamstercupid.love
- (1.53 MiB) Downloaded 411 times
Re: HamsterCupid: A toy component based entity system
I've added animations to HamsterCupid! Now, you can specify which animation the entity should be playing based on its state. This is done with a crude DSL. An entity has a film with a spritesheet, with different animations named by the state, and each animation has many frames.
So as an example, we have a person walking left that's 40x32 pixels with frames on rows 0 and cols 0 to 3.
However, declaring the frames one by one is pretty repetitive, so instead of using for loops, there's a short hand, where you specify how many consecutive columns after the first frame in the spritesheet to set:
If there are frames on two different rows belonging to the same animation, you can just declare that also:
Sometimes, spritesheets only have animations of the character walking to the left, but you want to also use it for walking to the right. If that's the case, we can specify a reflection on the frame.
Right now, animations and movements can be declared as a DSL, but I'll want to make it declarable in XML or JSON.
The code is at: https://github.com/iamwilhelm/hamstercupid
So as an example, we have a person walking left that's 40x32 pixels with frames on rows 0 and cols 0 to 3.
Code: Select all
local person = Entity:new("person", V:new(400, 200))
person.view:film("resources/personSpriteSheet.gif", function(view)
view:animation("walk.down", 40, 32, {}, function(animation)
animation:frame(0, 0)
animation:frame(0, 1)
animation:frame(0, 2)
animation:frame(0, 3)
end)
end)
Code: Select all
local person = Entity:new("person", V:new(400, 200))
person.view:film("resources/personSpriteSheet.gif", function(view)
view:animation("walk.left", 40, 32, {}, function(animation)
animation:frame(0, 0, { cols = 4 })
end)
end)
Code: Select all
local person = Entity:new("person", V:new(400, 200))
person.view:film("resources/personSpriteSheet.gif", function(view)
view:animation("walk.left", 40, 32, {}, function(animation)
animation:frame(0, 3, { cols = 2 })
animation:frame(1, 0, { cols = 2 })
end)
end)
Code: Select all
local person = Entity:new("person", V:new(400, 200))
person.view:film("resources/personSpriteSheet.gif", function(view)
view:animation("walk.left", 40, 32, {}, function(animation)
animation:frame(0, 0, { cols = 4 })
end)
view:animation("walk.left", 40, 32, {}, function(animation)
animation:frame(1, 0, { cols = 4, scale = Vector:new(-1, 1) })
end)
end)
The code is at: https://github.com/iamwilhelm/hamstercupid
Re: HamsterCupid: A toy component based entity system
Here's a demo video showing hamstercupid with an entity that has both a body and a head, animated while in different states.
The current DSL to declare this entity from a spritesheet is:
I currently don't yet have code for a state machine to help manage the different entity states and their transitions, but I'll be taking a look at kikito's Stateful lib to see if it fits my needs.
If any one has any feedback on the DSL and how useful or easier it might make your life, or the demo in general, I'd love to hear it, good or bad!
The current DSL to declare this entity from a spritesheet is:
Code: Select all
local person = Entity:new("person", V:new(x, y))
person.view:film("resources/dodgeball/wildlynx.gif", function(view)
view:animation("stand.down.left", 40, 32, {}, function(animation)
animation:frame(0, 0)
end)
view:animation("walk.down.left", 40, 32, { offset = V:new(0, 0), period = 1 }, function(animation)
animation:frame(0, 3, { cols = 3 })
animation:frame(1, 0, { cols = 3 })
end)
view:animation("stand.down.right", 40, 32, {}, function(animation)
animation:frame(0, 0, { scale = V:new(-1, 1) })
end)
view:animation("walk.down.right", 40, 32, { offset = V:new(0, 0), period = 1 }, function(animation)
animation:frame(0, 3, { cols = 3, scale = V:new(-1, 1) })
animation:frame(1, 0, { cols = 3, scale = V:new(-1, 1) })
end)
view:animation("stand.left", 40, 32, {}, function(animation)
animation:frame(0, 1)
end)
view:animation("walk.left", 40, 32, {}, function(animation)
animation:frame(1, 3, { cols = 3 })
animation:frame(2, 0, { cols = 3 })
end)
view:animation("stand.right", 40, 32, {}, function(animation)
animation:frame(0, 1, { scale = V:new(-1, 1) })
end)
view:animation("walk.right", 40, 32, { scale = V:new(-1, 1) }, function(animation)
animation:frame(1, 3, { cols = 3, scale = V:new(-1, 1) })
animation:frame(2, 0, { cols = 3, scale = V:new(-1, 1) })
end)
view:animation("stand.up.left", 40, 32, {}, function(animation)
animation:frame(0, 2, {})
end)
view:animation("walk.up.left", 40, 32, {}, function(animation)
animation:frame(2, 3, { cols = 3 })
animation:frame(3, 0, { cols = 3 })
end)
view:animation("stand.up.right", 40, 32, {}, function(animation)
animation:frame(0, 2, { scale = V:new(-1, 1) })
end)
view:animation("walk.up.right", 40, 32, {}, function(animation)
animation:frame(2, 3, { cols = 3, scale = V:new(-1, 1) })
animation:frame(3, 0, { cols = 3, scale = V:new(-1, 1) })
end)
end)
-- FIXME performance issue: addind child entity significantly degrades performance.
local head = Entity:new("head", V:new(0, -18))
head.view:film("resources/dodgeball/wildlynx.gif", function(view)
view:animation("look.down.left", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 0)
end)
view:animation("look.left", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 1)
end)
view:animation("look.up.left", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 2)
end)
view:animation("look.down.right", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 0, { scale = V:new(-1, 1) })
end)
view:animation("look.right", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 1, { scale = V:new(-1, 1) })
end)
view:animation("look.up.right", 32, 32, { offset = V:new(768, 0) }, function(animation)
animation:frame(0, 2, { scale = V:new(-1, 1) })
end)
end)
head.movement:addMovement(Motion.wiggle(0.5, 0.5, math.rad(270)))
person:addChild(head)
-- FIXME forgetting to initialize the state is causing bugs when writing the DSL
person.model.state = "walk.down.left"
head.model.state = "look.down.left"
If any one has any feedback on the DSL and how useful or easier it might make your life, or the demo in general, I'd love to hear it, good or bad!
- kikito
- Inner party member
- Posts: 3153
- Joined: Sat Oct 03, 2009 5:22 pm
- Location: Madrid, Spain
- Contact:
Re: HamsterCupid: A toy component based entity system
I think the dsl is just too verbose.
From what I've read.
It'll probably be a good idea to have animations in separate global variables. That way they can be shared between films:
Regards, and good luck with your project!
From what I've read.
- A film is made of 1 or more animations
- An animation is made of 0 or more frames
- In some cases the Animation's options parameter isn't needed. It would be nice if it could be removed
- Those "40,32" parameters are likely to be repeated in all cases, so I'd rather specify them once, and leave an option to change them (on the options params of each animation) if that was the case
Code: Select all
local person = Entity:new("person", V:new(x, y))
person.view:film(
"resources/dodgeball/wildlynx.gif",
40, 32, -- defaults
{
{ id="stand.down.left", frames={ {0, 0} } },
{ id="walk.down.left",
frames={
{0, 3, { cols = 3 } },
{1, 0, { cols = 3 } }
},
options={ offset=V:new(0, 0), period = 1 }
},
{ id="stand.down.right", frames={0, 0, { scale=V:new(-1, 1) } },
...
)
Code: Select all
local stand_dl = { id="stand.down.left", frames={ {0, 0} } }
local walk_dl = {
id="walk.down.left",
frames={
{0, 3, { cols = 3 } },
{1, 0, { cols = 3 } }
},
options={ offset=V:new(0, 0), period = 1 }
}
local stand_dr = { id="stand.down.right", frames={ 0, 0, { scale=V:new(-1, 1) } }
person.view:film(
"resources/dodgeball/wildlynx.gif",
40, 32, -- defaults
{ stand_dl, walk_dl, stand_dr ... }
)
When I write def I mean function.
Re: HamsterCupid: A toy component based entity system
Hey there, thanks for the feedback. I'll defn take your suggestions into account as I'm refining it. Thanks.
Wil
Wil
Who is online
Users browsing this forum: No registered users and 0 guests