Scene

The cave.Scene is where all your game will happen. You can think of it as a Level where you can put all your entities in order to build your worlds, enemies and the game itself.

There will always be a scene loaded (and you can get the current one by calling cave.getCurrentScene()), one at the time and you can change or reload it any time you want during the gameplay. Check the Engine > Utilities > Scene section for more details on it.

In order to completely understand and use all the APIs provided to manipulate the Scenes, you'll also see some other important classes (in this page) that will make a lot of sense to you after reading the cave.Scene documentation below. So let's get started:

cave.Scene

Variables

Cave's Scene only have those three variables. But they all serve a specific purpose, as you can see below.

Variable Description
name The scene name.
paused You can set this to True or False to pause/unpause the scene. If True, it will no longer call the component's update method and will call the pausedUpdate instead.
properties This is a python dictionary that you can use to store everything related to your game's scene. Very useful for variables that needs to be accessed by various entities, such as enemy waves, time spent, remaining items to collect or scores.

Here is the Python API for Reference:

name        : str
paused      : bool
properties  : dict
Methods

In order to create new Entities, you have two options: Build them from scratch in the code or instantiate them by providing a template. To create them from scratch, you can use one of those two scene methods:

# Creates a brand new entity and returns it to you:
newEntity() -> cave.Entity

# If you already have an Entity and just need to add it to the scene, then use this 
# (it will return the entity as well to make it easy for you to use the method):
add(entity: Entity) -> cave.Entity

The scene.add(entity) method is used to add an entity you manually build by yourself in the code. Most of the times it's not exactly what you want. That's why there is a scene.addFromTemplate(name) alternative: Given an Entity Template name, the engine will construct a new entity for you followying that template. Both methods returns the added entity for convenience.

If you want to create a new one by providing a template (recommended), use this method:

# Note that you can provide optional arguments to set a custom transform to add it to.
addFromTemplate(
    templateName : str, 
    position = cave.Vector3(0, 0, 0), 
    rotation = cave.Vector3(0, 0, 0), 
    scale    = cave.Vector3(1, 1, 1)
) -> cave.Entity

If you need to remove an Entity from the scene, you can either kill it (calling entity.kill()) or just remove them directly using this method:

remove(entity : cave.Entity)

This method will return the elapsed time, in second, since the Scene creation. This value does not include the time paused (and yes, it's the number user by the SceneTimer to count time).

getElapsedSceneTime() -> float

You may also want to adjust every scene setting that you see in the Scene's editor, such as the atmosphere, mist, sky, etc. For that, you need to get the Scene's GraphConfig class instance. You can check their API reference later in this page. Here is how you can get it:

getGraphConfig() -> GraphConfig

In Cave Engine, every Scene have its own Sun light and you can get it by calling this scene method:

getSun() -> cave.Sun

You can also get the Scene's active Camera. It's importat to know that the scene stores its own camera instance (type cave.Camera) and it has nothing to do with the CameraComponent or the entity that have it. Check the camera operations later in this page.

getCamera() -> cave.Camera

- Scene Collision Checks

If you need to do some "in place" Collision dettection like a ray cast or check if there are objects at certain place, you can use one of those methods below. Keep in mind that all of them will rely on the physics engine to return the correct values, so make sure that the Entities that you're trying to detect have a proper Physics shape (like a Rigid Body or a Character Component).

It's also good to know that the result is calculated immediately when you call those functions, so you don't need to worry about the output being delayed by a frame like some engines. It will be up to date with the current scene status.

The first and most common method in this list is the rayCast and rayCastAll. The difference between them is that the first one will stop and return in the first ray hit and the second one will return a python list with all the hits. It's a good option if you want to make a gun that can shoot through walls, for example.

rayCast(origin : cave.Vector3, target : cave.Vector3, mask=cave.BitMask()) -> cave.RayCastOut
rayCastAll(origin : cave.Vector3, target : cave.Vector3, mask=cave.BitMask()) -> list of cave.RayCastOut

Notice that it does have an optional mask parameters (cave.BitMask) that allows you to filter your results.

Sometimes you may want even more control than a simple RayCast. They may be a good fit to shoot bullets, for example, but let's think a little bit out of box: Let's think about Goku and his famous attack: The Kamehameha.

In this attack, Goku first creates an energy ball into his hands, then throws the energy against his target, in a form of a giant energy wave. If we wanted to recreate this effect in Cave, in order to apply the attack's damage, we could cast a Ray using the RayCast and check for the hits. But this solution have a problem: The Kamehameha is a thick evergy wave and the RayCast will consider it as if it was just a thin line. If Goku decided to attack a wall with it and the wall has a small hole, like a bullet hole, the kamehameha would go through if we user the RayCast and that's not what we want.

So what to do instead? It would be great if we could add a radius to the RayCast, that way we could make it as thick as we want, right? Exactly! And that's the purpose of the SphereCast. It's basically a RayCast, but with a radius. This alternative is very useful in many other use cases, not only the Kamehameha. As another example, GTA IV is a game that seems to use this technique to handle its camera collision.

(If you want to be technical here and understand how it works behind the scenes: it does a convex sweep test with a sphere of a given radius from and to a position in the physics world).

The Sphere Cast, just like the Ray Cast, have two methods related to it: sphereCast(...) and sphereCastAll(...) and they behave in the exact same way as their ray cast equivalents, except that they return cave.SphereCastOut class instances instead of the cave.RayCastOut. You can find its documentation in this page for reference. Here is the Sphere Cast API reference:

sphereCast(origin : cave.Vector3, target : cave.Vector3, radius: float, mask=cave.BitMask()) -> cave.SphereCastOut
sphereCastAll(origin : cave.Vector3, target : cave.Vector3, radius: float, mask=cave.BitMask()) -> list of cave.SphereCastOut

Ray and Sphere casts are good, but sometimes they are not enough for what you want. That's why Cave Engine lets you check for collision within certain bounds. For now, you can use a Box or a Sphere shape for this and the usage is very simple as you can see below. Check the cave.CollisionInfo for additional details.

# Box Shape checks:
checkContactBox(boxTransform: cave.Transform, mask=cave.BitMask()) -> list of cave.CollisionInfo
checkContactBox(
    boxPosition : cave.Vector3,
    boxRotation : cave.Vector3,
    boxScale    : cave.Vector3, 
    mask=cave.BitMask()
) -> list of cave.CollisionInfo

# Sphere Shape checks:
checkContactSphere(position: cave.Vector3, radius: float, mask=cave.BitMask())-> list of cave.CollisionInfo

- Scene Queries

If you want to get a specific Entity in the scene, you can use one of those two methods:

getEntity(id : int) -> cave.Entity
getEntity(name : str) -> cave.Entity

Note: Time complexity of getting an Entity by name (string) is O(n) and by ID is O(1). In other words: get by name is way slower than by ID so it's recommended to store the entity ID and use it as much as you can. Also, when you have multiple entities with the same name in the scene, it will always return the first entity found (and this result may be non deterministic - in other words the result may vary).

But that's not everything! If you need to get all the entities in the scene, you can do it so by calling:

getEntities() -> list of cave.Entity

And you can be a bit smarter than that and filter the Entities by something, such as by Tag or Property. Keep in mind that if you decide to filter by Tag, it will tend to be faster than it is by Property, since the Cave's tag system does not rely on python (meaning that the backends are completely written in C++) and the Properties does.

getEntitiesWithTag(tag: str) -> list of cave.Entity
getEntitiesWithProperty(property: str) -> list of cave.Entity

At this point, you probably already noticed that Cave Engine organizes the scene into a Tree structure, where entities can be child of other entities. All the query methods mentioned above will consider this entire hierarchy, but sometimes you may only need what we call the Root Entities. Meaning the Entities with no parents. If that's your case, you can easily retrieve them with the methods above:

# This will return all the root entities in the scene:
getRootEntities() -> list of cave.Entity

# The following ones will only query the root Entities:
getRootEntitiesWithTag(tag: str) -> list of cave.Entity
getRootEntitiesWithProperty(property: str) -> list of cave.Entity

Good to know: If you put an entity inside a folder, it will no longer be a root entity (because it will be child of the folder entity). It's also good to know that querying Root entities can be faster than doing it with all entities, since the engine does not need to recursively iterate the root children.


Scene Related Classes

As discussed in the beginning of this page, there are some other helpful classes that are important for you to understand in order to properly manipulate the scenes in Cave. Here you'll find their documentation.

cave.RayCastOut

This class is used as a data storage to return ray cast hit information. Returned by the scene when you do a rayCast and it finds something.

Variables
Variable Description
hit It's True if this is a valid RayCast Output. It's always good to check it first. As an example, of the scene.RayCast(...) method don't find anything, it will return an invalid RayCastOut and this will be false.
position The exact position in world space where the ray hitted an entity. Good to use for bullet holes, as an example.
normal The normal that the surface hitted by the ray is pointing to. Also good for bullet holes, as an example.
entity The entity hit by the ray.

Here is the Python API for Reference:

hit      : bool
position : cave.Vector3
normal   : cave.Vector3
entity   : cave.Entity

cave.SphereCastOut

This class inherits from cave.RayCastOut (meaning that it have all its parent class methods and variables) but add two extra variables: penetration and recoveryPosition. It serves as the output for the Scene's SphereCast methods.

Variables
Variable Description
penetration Ranges from [0.0 to 1.0] and specifies how deep into the shape the collision is. If it's near zero, it means that the collision was close to the SphereCast's border and if it's near to one it means that it was close to the spherecast's center ray.
recoveryPosition This represents the maximum position that the sphere could be at before hitting an obstacle.

Here is the Python API for Reference:

penetration      : float
recoveryPosition : cave.Vector3

cave.CollisionInfo

This class is used as a data storage to return collision hit information. This class is returned by the Scene when you call methods like scene.checkContactBox(...) or scene.checkContactSphere(...), but it is also used anywhere else where collision information is necessary, for example when you check if a Rigid Body or Character Component collided with something.

There are two important things you need to know about Collisions and the CollisionInfo:

  • The first thing is that when a shape collides with another, it may have one or more hits, depending on how deep the collision was and a lot of other factors such as the shape, how the collision happened and so on.

  • The second is that, because of this behavior, most of the times where you expect a CollisionInfo from Cave, it will always return a list of them (even if there is only one element in it), because it may have multiple ones, as explained above. Due to that, different from the cave.RayCastOut, you'll not find a hit variable to check if the collision was valid, because the engine will never return an invalid one. If there is no collision, the list will simply be empty.

This is information will help you understand how to properly manipulate the Collisions in the engine. Most of the time that you need to handle them, you'll probably want to iterate over the output collisions until you find the one you want. Here is an example:

scene = cave.getCurrentScene()
playerPos = self.entity.getTransform().position

for hit in scene.checkContactSphere(playerPos, 3.0): 
    print("New Entity hit:", hit.entity.name)

In the code above, we use the scene's checkContactSphere method to see if there are any Entities within 3 meters from the player position (considering that it's the player that's executing this code, of course). Note that if you run this code, it may print the same entity twice, due to the reasons explained beefore. If you only want to take an entity hit into account once, you can cache them into a list, like this:

alreadyHit = []
for hit in scene.checkContactSphere(playerPos, 3.0): 
    if hit.entity in alreadyHit:
        # Skipping any entity that we already checked!
        continue 

    # Now we'll add the entity to the list to make sure we don't check it twice:
    alreadyHit.append(hit.entity)

    # Finally, we do what we want with the entity:
    print("New Entity hit:", hit.entity.name)

The explanation above will help you to handle collisions in most of the situations where the engine outputs to you a list of cave.CollisionInfo instances.

Variables
Variable Description
position The exact position in world space where the collision happened.
normal The direction that the collision point is pointing to.
entity The entity hit.

Here is the Python API for Reference:

position : cave.Vector3
normal   : cave.Vector3
entity   : cave.Entity

cave.Camera

The actual camera used by the Scene to do the rendering. You can get it by calling scene.getCamera(). This class inherits from Cave's Transform, so you can also expect to be able to do all sorts of Transform manipulation with it, such as moving, rotating and/or scaling it.

Variables
Variable Description
aperture Camera's aperture
nearPlane The minimum distance from the camera that gets drawn to screen.
farPlane The maximum distance from the camera that gets drawn to screen.

Here is the Python API for Reference:

aperture  : float
nearPlane : float
farPlane  : float
Methods

The Camera have a lot of useful methods as well:

getViewProjection() -> cave.Matrix4

# Returns a Vector representing a Ray that could be casted from the camera to the screen coordinates specified in the arguments.
getScreenRay(float x, float y) -> cave.Vector3

# This one does the opposite of getScreenRay: Given a world position, returns its projected position in the camera's screen view.
getScreenPos(worldPos : cave.Vector3) -> cave.Vector2

You can use the methods bellow to set the camera to Perspective or Ortographic mode:

setPerspective(fieldOfView : float)
setOrthographic(orthoArea : float)

And, of course, you can also get the field of view (or orthographic area) back by calling those method bellow. Keep that in mind that if you have a Orthographic camera dn tries to get the perspective field of view (or vice-versa), you may get wrong numbers or undefined behaviour.

getPerspectiveFov() -> float
getOrthographicArea() -> float

If you want to check if a camera is either perspective or orthographic, you can use those methods:

isPerspective() -> bool
isOrthographic() -> bool

cave.Sun

As already explained in this page, every Scene in Cave have its own Sun light. You can get it by calling scene.getSun() and it will return an instance of this class. You can use it to adjust the look of it, as you can see below.

Variables
Variable Description
hour The hour of the day used to determine the sun position. Goes from 0 to 24 and the minutes needs to be represented by the fractional part of the value, so 12:30h is 12.5.
angle The angle represents where in the horizon the sun will rise and set. It goes from to 360º, in degrees.
color The color of the sun.
mask The shadow mask. It will only cast shadows to Mesh Components that its shadow mask intersect with this. If you're trying to make an entity don't cast shadows, this is what you're looking for.
intensity The sun intensity. The more, the brighter it will be.

Here is the Python API for Reference:

hour      : float
angle     : float
color     : cave.Vector3
mask      : cave.BitMask
intensity : float

Scene's Graphic Settings

As discussed already, most of the scene's graphic settings (such as mist, atmosphere and so on) can be adjusted by getting the cave.GraphConfig class from the Scene (by calling the scene's getGraphConfig() method). Since this class is configuration only, I'll simplify this API to make it easier for you to use. All the settings have the same purpose as they do in the Scene's Editor UI in the Editor. Here is the full API for reference.

cave.GraphConfig

Variables
applyShading      : bool

# Ambient Related...
ambient         : GraphConfigAmbient
ambient.use     : bool
ambient.factor  : float
ambient.color   : ColorSampler

# Mist Related...
mist          : GraphConfigMist
mist.use      : bool
mist.start    : float
mist.distance : float
mist.falloff  : float
mist.color    : ColorSampler

# Sky Related...
sky           : GraphConfigSky
sky.blur      : float
sky.intensity : float
sky.angle     : float
sky.color     : ColorSampler

# Ambient Occlusion Related...
ao            : AmbientOcclusion
ao.use        : bool
ao.texelScale : float
ao.distance   : float

# Atmosphere Related...
atmosphere              : GraphConfigAtmosphere
atmosphere.planetRadius : float
atmosphere.atmHeight    : float
atmosphere.sunIntensity : float

# Misc...
background       : cave.Vector3
ambientCol       : cave.Vector3
ambientIntensity : float