-
Notifications
You must be signed in to change notification settings - Fork 3
Creating a simple game
Capsian is not "production ready", but you can already use it to do small things.
NOTE: Most examples use engine.main_camera, avoid using this in the on_start method to prevent problems!
Create a python file inside the scripts folder and import it from scripts/__init__.py
. Remember, the working directory is the one with the main.py
file, so always put scripts.
when importing your scripts.
Inside the newly created script, there are a few things you should do before even thinking about the game.
- Create a PerspectiveCamera
- Create a Window3D
from Capsian import *
from addons import *
camera = PerspectiveCamera()
window = Window3D(
camera, # Tells the window to use the new camera
vsync=False, # Disables Vertical Sync
width=1280, # Sets the width to 1280 px
height=720 # Sets the height to 720 px
)
It is recommended that you use just on Script component per file, these can get pretty messy. If you want/need an example on how to organize your code, look here.
Unless you find some other way to keep track of the current camera and window, you can use the integrated tools.
engine.main_camera
is the current camera, while engine.main_window
is the current window.
If niether of these objects are declared, the respective vars will be set to None and the game will probably crash.
You can also use engine.default_clock
to access the main Clock
object.
A scene is not a scene if it is empty. Capsian offers a small, but customizable and flexible, array of entities to choose from. Cube, Particles2D and Square are the most used.
A Cube is a very simple entity, though it requires a bit of "know-how" to create. To create a cube, you need:
- A Texture Object (
Texutre3D
orSmartTexture3D
) - A Capsian Material (
Material
) - A Transform Component (
Transform
) - A 3D Scene (
Scene3D
)
from Capsian import *
from addons import *
@IndependentComponent
class GenerateCube(Script):
def on_start(self, time) -> None:
# 1. Loading the texutre from a file
tex = Texture3D("filepath")
# 2. Creating a material
mat = Material(tex)
# 3. Creating the Scene
s = Scene3D(engine.main_camera)
# 4. Create the actual cube
cube = Cube(
Transform(
x=0,
y=0,
z=0,
width=1,
height=1,
depth=1
),
s,
mat
)
Creating a square is much easier than creating a cube. It does not require any texutre, nor material, only a scene.
from Capsian import *
from addons import *
@IndependentComponent
class GenerateSquare(Script):
def on_start(self, time) -> None:
# 1. Create the scene
s = Scene3D(engine.main_camera)
# 2. Create the square
square = Square(
Transform(
x=0,
y=0,
z=0,
width=1,
height=1
),
s
)
Creating a textured square is basically the same as creating a cube, except you don't need a material
from Capsian import *
from addons import *
@IndependentComponent
class GenerateCube(Script):
def on_start(self, time) -> None:
# 1. Loading the texutre from a file
tex = Texture3D("filepath")
# 2. Creating the Scene
s = Scene3D(engine.main_camera)
# 3. Create the textured square
cube = Cube(
Transform(
x=0,
y=0,
z=0,
width=1,
height=1
),
s,
tex
)
A rotating square is the same as a square, except it always points towards the camera.
Particles are nothing more than moving and rotating square. Using the Particles3D
entity, though, saves you a lot of code.
In this example, a looping function is created in order to create infinite particles, but you can do it however you want.
from Capsian import *
from addons import *
@IndependentComponent
class GenerateCube(Script):
def on_start(self, time) -> None:
@engine.default_clock.Schedule.loop(interval = 0.3) # Seconds
def generate_particles(delta: float) -> None:
s = Scene3D(engine.main_camera)
Particles3D(
Transform(
x=0,
y=0,
z=0,
width=0.25,
height=0.25
),
3, # Amount of squares to generate
240, # How long each batch should last
s
)
An entity alone is notvery useful. As you probably noticed, a "Transform Component" is required by basically every entity in Capsian. Transforms are just one of 3 main components in Capsian. their role is to store information about the position, rotation, direction and size of their parent entity. Transforms, and components for that matter, are used since they can add functionality to any entity, instead of such functionality being hardcoded.
The other components are
A Character Controller component allows you to move the parent entity as it were the player. It is usually attached to a PerspectiveCamera in order to move it and create an FPS Controller.
from Capsian import *
from addons import *
@IndependentComponent
class CameraMovement(Script):
def on_start(self, time) -> None:
cam = engine.main_camera
cam.components.add(CharacterController())
@engine.default_clock.Schedule.loop(interval = 0.3) # Seconds
def move_camera_up(delta: float) -> None:
controller = cam.components.character_controller
controller.move(Direction.UP)
Lights in Capsian are components, thus they need to attached to an entity in order to be usable.
from Capsian import *
from addons import *
@IndependentComponent
class LightHandler(Script):
def on_start(self, time) -> None:
s = Scene3D(engine.main_camera)
# Create a random entity to add an AmbientLight
Entity(scene=s).components.add(AmbientLight(Color(255, 0, 0).rgba))
A Keyboard Input Handler Component is used to get input from the keyboard.
from Capsian import *
from addons import *
# With this, make sure the entity you're attaching the component to is active. If it is not, then you won't be able to use the on_key_held method
@camera.components.component()
class MyKeyboard(KeyboardInputHandler):
def on_key_press(self, symbol, modifiers) ->None:
# What should happen when a key is pressed.
# To check what key has been pressed, just do if symbol == Key.NAME (Like Key.A)
def on_key_released(self, symbol, modifiers) -> None:
# What happens when a key is released
# To check what key has been pressed, just do if symbol == Key.NAME (Like Key.A)
def on_key_held(self, keys: dict) -> None:
# What happens when one or more keys are pressed (Usually used for movement)
# Since keys is a dict, you can use if keys[Key.NAME] == True (You can cut the == True)
A Mouse Input Handler Component is used to get input from the mouse.
from Capsian import *
from addons import *
@IndependentComponent
class MyMouse(MouseInputHandler):
def on_button_press(self, x, y, button, modifiers) ->None:
# What should happen when a mouse button is pressed.
# To check what key has been pressed, just do if button == MouseButton.NAME (Like MouseButton.LEFT)
def on_button_released(self, x, y, button, modifiers) -> None:
# What should happen when a mouse button is pressed.
# To check what key has been pressed, just do if button == MouseButton.NAME (Like MouseButton.LEFT)
def on_button_held(self, buttons: dict) -> None:
# What happens when one or more buttons are pressed (Usually used for movement)
# Since buttons is a dict, you can use if buttons[MouseButton.NAME] == True (You can cut the == True)
Building your game into an executable is realy easy thanks to pyinstaller.
To build your game open CapsianLine via the console.py
file, and perform the app build
command.
This should generate an executable in the dist
folder. This executable must be in the same directory as options.cpsn
and your resource files (Like textures and sounds).
If you're experiencing issues with Windows Smart Screen or other anti-malware software, you can try adding the -- True
flag to the app build
command (app build -- True
). This will build an executable that depends on other files. Single-file executables, in fact, are not well seen by anti-malware software due to their widespread use in viruses. Thus, distributing a multi-file executable may help.