Skip to content

Creating a simple game

tzyvoski edited this page Mar 26, 2021 · 5 revisions

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!

Quick Overview

Getting started

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.

  1. Create a PerspectiveCamera
  2. Create a Window3D

Example

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.

Access

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.

Entities

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.

Quick Overview

Cube

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 or SmartTexture3D)
  • 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
        )

Square

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
        )

Textured Square

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
        )

Rotating Square

A rotating square is the same as a square, except it always points towards the camera.

Particles

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
            )

Entity Components

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

Character Controller

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)

Light

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))

Keyboard Input Handler

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)

Mouse Input Handler

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)

Distributing Your Game

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.