First of all, in order to access the classes and functions inside a ECSpy file you'll need to import it like
from ECSpy.File import thingFromtheFile- In your main file musn't create an App() you need to import ECSglobals.mainApplication. This is your games App()
To start off, import your main application
from ECSpy.ECSglobals import mainApplication as appThis is just nicer to work with.
Now ECS is split into 3 things
- Entities
- Components
- Systems
ECSpy has all these 3 things.
Systems in ECSpy are just python functions that you pass to the app class.
They dont take any arguements. For example:
#Here you can see this is just a normal python function
def mySystem():
passTo add this to the application you must use .addSystem
from ECSpy.ECSglobals import mainApplication as app
def mySystem():
pass
app.addSystem("Setup",mySystem)The "setup" is the sytem type you assign to the system when you add it
-
"Update" systems are run everyframe (in the order they were added)
-
"Setup" systems are run once at startup (again in the order they were added)
Now currently the system doesnt do anything
To make it do something we must talk about components
Components are simple classes that inherit from the ECSpy.Component.Component class. They store only data.
The base component class has only one attribute, name. the name is the string used to identify the component inside of entities.
An example would be
from ECSpy.Component import Component
class myComponent(Component):
def __init__(self,myDataProperty)
#The name of the component must be assigned
super().__init__("myComponent")
self.myDataProperty = myDataPropertyComponents can have methods attached to them, but the base Component class has only one method. That being mut()
Its use case is as such
myComp = myComponent(myDataProperty = 0)
myComp.mut("myDataProperty",myComp.myDataProperty+1)
#This increases the myDataProperty of myComp by one
#This is very important to use consistently when mutating component properties.Entities are objects that store a group of components alongside a unique ID
To make them you have to use app.world.addEntity()
# -- import app up here
from ECSpy.Entity import Entity
app.world.addEntity(Entity())This is fine but this creates an empty entity with no components. To add components use need to use the Entity's addComponent method.
app.world.addEntity(Entity.addComponent(myComponent(0)))this creates an entity with a myComponent component.
To use these together lets talk about how to actually modify entities at runtime and such
# -- imports up here
class Name(Component):
def __init__(self,namestring):
super().__init__("Name")
self.namestring = namestring
def printNames()
pass
person1 = Entity().addComponent(Name("Patricia"))
app.world.addEntity(person1)
app.addSystem("Startup",printNames)this will be our base. If you run this you will get no output because we have left the printNames system blank.
In order to print the names you need to get all the entities with a Name component. To do this you'll need to use ECSpy.Query.callComponents()
from ECSpy.Query import callComponents
from ECSpy.Query import Query
def printNames():
# callComponents takes a (returns) and (dependencies)
names = callComponents(Query(returns = ("Name",), dependencies = ()))call components will return a dict of entities and theyre attached components. The list of components will be only the entities components in the returns of the query. So in this case we will get
{
(the id of an entity) : Name()
}
If we had put any dependecies we wouldve gotten
{
(the ID of an entity with the dependency) : Name()
}
now we need to go through each entity in the dict
def printNames():
names = callComponents(Query(returns = ("Name",), dependencies = ()))
for entity in names.keys():
for name in names[entity]:
print(name.namestring)with these changes, when we run the program now we will get an output of
patricia
Yippee!! it works!!!
Now lets try to modify its value.
def changeNames():
names = callComponents(Query(returns = ("Name",), dependencies = ()))
for entity in names.keys():
for name in names[entity]:
#use mut to modify namestring
name.mut("namestring", "Jimmy")
#addSystem can accept a tuple of systems aswell
app.addSystem("Startup",(printNames,changeNames))Now heres a distinction. If you run this now you will still get the same output of patricia.
patricia
This is because we add
- printNames
- then changeNames
so the names get changed after we print the names. This is because the systems get ran in the order they are added.
So for our code to work properly we need to change it to
app.addSystem("Startup", (changeNames,printNames))and now we get out desired output!
Jimmy
Plugins are python functions that modify the app. These are usefull for not having to have all your setup in your main.py. They take the app as the only parameter. An example would be.
def myPlugin(app: App()):
app.addSystem("Startup",myStartupSystem)
app.addSystem("Update",(myUpdateSystem1,myUpdateSystem2))to use this you will have to import the function from whatever file its in. And then call app.addPlugin()
from myfile import myPlugin
#This can also take a tuple of plugins to add aswell, just like addSystem
app.addPlugin(myPlugin)Resources are globally accessable components that are stored as part of the App() class and can be used by any system, anywhere.
To create a resource you do.
from ECSpy.Resource import Resource
class myResource(Resource):
def __init__(self,myProperty : int):
super().__init__("myResource")
self.myProperty = myPropertyThis is the same process as defining a componenet, but instead of inheriting from the base component class, you inherit from the base resource class.
In order to use your resource, you must add it to the application by doing:
#-- import mainApplication as app up here
app.addResource(myResource)To access your resource, you can then, inside of a system call
#-- import mainApplication as app up here
def mySystem():
x =app.getResource("myResource").myPropertyLike mutating component, to mutate a resource property you must also use the .mut() method:
def mySystem():
app.getResource("myResource").mut("myProperty",1)Now that you know all of this, we can start making an actual application. We're going make a basic empty window, that you can expand on later. This is also the program inside the main.py file of the project, as a base. Note that this uses pygame-ce as its api for input and output. If you dont know anything about pygame check out https://pyga.me/docs/#tutorials
In order to make a window, were going to use the prelude module inside of ECSpy. This module does some basic pygame setup for us. Like starting up a basic event handler and initializing pygame.
Lets start out with creating out games data. This data is what will be used by the prelude module, to create a window of your desired size.
GameData is a resource so you have to import it first.
from ECSpy.Prelude import GameDataThen you have to add the resource to your app. GameData takes 2 parameters, being screenSize->(width,height) and caption->"caption". These will be the size of the window (and its resolution) and the window caption.
# -- import mainApplication as app here
app.addResource(GameData(
screenSize = (300,300),
caption = "ECSpy"
))Now we've defined out game data. We can add the preludPlugin to our application, this plugin will create a Screen resource and a basic handleEvents system to create a window.
# --import app and add gamedata up here
from ECSpy.Prelude import preludePlugin
app.addPlugin(preludePlugin)the final piece is to call app.run() at the end
app.run()If you run this now you should get a 300*300 black window!
What we now have is a Screen resource and an eventHandler system.
By the handleEvents system can be found in ECSpy.Events and is
def handleEvents():
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
app.getRescource("Clock").Clock.tick(60)This both handles some basic events, add ticks the pygame clock.
You can change this to fit your own needs however.
The Screen resource has only one property, that being Screen.screen. screen being a the pygame Surface of our window.