gamengine3d is a python pypi package designed to make games in 3d with python simple.
You can view the youtube tutorial here:
To get started, first install gamengine3d with:
The vector3d class
The vector3d class is a class that holds a 3 dimensional vector with x, y, and z components. To create a vector3d instance, you can use vector3d(x value, y value, z value), and you can access the instance's .x, .y, and .z attributes. If only 1 argument is provided, such as vector3d(60), then all 3 components will be set to that value. If 2 arguments are provided, the third argument is set to 0 by default. By taking v as vector3d(1, 4, 3), the vector3d class supports:
magnitude as a property that gets the magnitude of the vector -- v.magnitude
sqr_magnitude as a property that gets the squared magnitude of the vector -- v.sqr_magnitude
normalize as a method that normalizes the vector in-place -- v.normalize()
normalized as a property that returns a new normalized vector -- v.normalized
dot product as a method that takes another vector and returns the dot product of those vectors -- v.dot(other vector3d object)
cross product as a method that takes another vector and returns the cross product of those vectors as a vector3d -- v.dot(other vector3d object)
addition of 2 vectors that adds each component of 1 vector to the corresponding component of the other -- v + other vector3d object
addition of a vector and a scalar that adds the scalar to each component of the vector -- v + scalar
subtraction of 2 vectors that subtracts each component of 1 vector from the corresponding component of the other -- v - other vector3d object
subtraction of a vector and a scalar that subtracts the scalar from each component of the vector -- v - scalar
multiplication of 2 vectors that multiplies each component of 1 vector with the corresponding component of the other -- v * other vector3d object
multiplication of a vector with a scalar that multiplies each component of the vector with the scalar -- v * scalar
division of 2 vectors that divides each component of 1 by the corresponding component of the other -- v / other vector3d object
division of a vector by a scalar the divides each component of the vector by the scalar -- v / scalar
converting the vector into a tuple as method -- v.totuple()
creating a vector3d object from a tuple as a method -- vector3d.fromtuple(tuple with x, y, and z values)
copying a vector3d object and returning into another variable as a method -- v.copy()
negating the vector that multiplies all the components by -1 -- -v
The vector3d class has a built in __repr__ method that returns a string like vector3d(x value, y value, z value). You can also use vector3d.up as a property to get a vector3d object pointing upwards. same for down, right, left, forward, and backward. You can also use vector3d.one to get a vector3d object with all 1s for each component, and same for vector3d.zero.
The Color class
The Color class is a class that has built in colors and functionality. It has an r, g, and b value for each instance. After creating a Color instance, you can access its .r, .g, and .b values. You can create a color class with rgb values using :
Color(r value, g value, b value).
The Color class supports:
getting color from hex values as a method -- Color.hex(hex code starting with #)
comparing Color instances to see if they are the same color -- color_instance1 == color_instance2
converting to hex as a method -- color_instance.to_hex()
converting to rgb as a method -- color_instance.to_rgb()
The Color class also has built in colors like using Color.color_name, like:
Color.black
Color.white
Color.red
Color.green
Color.blue
Color.yellow
Color.cyan
Color.magenta
Color.grey
Color.light_grey
Color.light_red
Color.light_green
Color.light_yellow
Color.light_blue
Color.orange
The Light class
The Light class is used to create and store a light object in the scene. You can create a light instance with:
light = Light(position=vector3d object, color=Color-object, intensity=value from 0 to 1, show=bool)
The position argument will be the position of the light in the scene. The color argument is the color of the light. the intensity is basically how bright the light is. the show is a boolean, if true, it will draw a sphere at the position of the light with the color of the light.
The Cuboid class
The Cuboid class is used to create and store a cuboid object in the scene. You can create a Cuboid object like:
The pos is the position of the cuboid in the scene, the size is the size of it, the color is the color of the cuboid, the name is the name for that cuboid, which will be used to access it and send messages to it, the rotation is in degress within a vector3d object.
The Engine class
The Engine class is used to create the actual game engine object in order to create the game. It creates a new window with the scene when the instance's run() method is called. While creating an instance, the first argument is the width of the window (px) that will be created, the second argument is the height of the window (px), the resizable argument allows the user to select whether the window should be resizable or always have the same dimensions. The name argument becomes the name of the window created. the background_color argument is set as the background color for the window.
gamengine3d includes a CLI (Command Line Interface) With built in examples. You can use it with
To List all the available demos. It will look something like this
You can look at the names in the braces under the positional arguments section. As of now, there are 4 built in demos. They are basics, messages_demo, player_movement, and the_everything_demo.
The Basics Demo
To use the basics demo, you can type:
You will see a new window, with the name "Basics Demo", with a width and height of 500x500px. It should look like this:
You can zoom in and out using the scroll wheel with the mouse, or the 2 finger gesture on a touchpad. And pan around by left clicking and holding while dragging. You can rotate the camera by right clicking, holding, and dragging.
Lighting and shadows are built into the engine. You just need to add the objects. This demo shows a simple window, with a dark grey floor and a light above it. The code looks like this:
Explanation:
The Engine, Cuboid, vector3d, Light, and Color classes are imported from gamengine3d.
The engine instance is made, with a window of 500x500px, with light blue as the background color, and .4 as the ambient light.
The floor object is created using the Cuboid class, with a position of vector3d.zero, a size of vector3d(10, .5, 10), and a color of light_grey.
A light is created with a position of vector3d(0, 4, .1), and a color of white.
The light is added to the engine using engine.add_light()
The floor is added to the engine using engine.add_object(floor).
The game is then ran using the engine.run() method.
The Player Movement Demo
To use the player movement demo, you can type:
You will see a new window, with the name "Player Movement Demo", with a width and height of 1200x800px. It should look like this:
You can then use A, S, W, and D for moving the Player around (the red cube). You can also use the spacebar for jumping.
The code looks like this:
Explanation:
The necessary classes are imported from gamengine3d.
the engine instance is created
the room is created by defining with size, half_size, and color
the walls and floor of the room are created with the appropriate sizes, colors, and names
the player is created with a name of "Player" and a size of vector3d(.2)
the player's script's path is gotten
a script is attached to the player, while also passing the engine's context to the attach method
the player object is added
lights are created and added.
the game is ran
The scripts/player_movement.py code looks like this:
Explanation:
The necessary classes are imported from gamengine3d.
A new class called PlayerMovement is made (it has to be the PascalCase version of the filename)
The __init__ method is defined to take 2 arguments, the object of type Cuboid, and context of type Context. since you attached the script to the player, the script now has access to the entire player object through the obj argument passed.
self.obj is assigned to obj
self.context is assigned to context
the speed and rotate speed are defined in units per second and degrees per second respectively
self.gravity is defined using a vector3d where the y value is -4.8. self.velocity is defined as vector3d.zero
movement bindings are declared using context.on_key_held, which takes a key as a str, and callback as a callable type, and dt as a bool, and args as a list. Once that key is detected to be pressed, the callback is called with the arguments, and dt if it is True.
the object's rotation is set to 0 degrees
the update method is defined and takes dt and passes it to self.handle_gravity while passing dt (This is called every frame)
The handle controls method is defined, which takes dt, and direction as an argument.
handle_controls then gets the forward vector of the object's rotation using the get forward function.
handle_controls then checks if the direction is forward, and then calculates delta_pos, which is then added to the object's current position. The program then uses self.context.functions.is_colliding with the name of the object, and the walls to check whether the object is touching the walls. if the object is touching any walls, then delta_pos is subtracted from the object's position to undo the move.
handle_controls does the same thing for if direction is backward but subtracts delta_pos, then adds to undo it
handle_controls checks if the direction is left, and if so it decreases the obj's rotation.z by the rotation speed * dt
handle_controls does the same thing for if direction is right, but instead adds it
handle_controls then checks whether the direction is up, and if so, it will check whether the object is on the floor using context.functions.is_colliding_pos, and if it is, it will set the velocity in the y direction to 3, and the object's y position is incremented by .1 units to ensure gravity is applied next frame.
the handle_gravity method is defined, and takes dt
handle_gravity then checks whether the object is grounded using context.functions.is_colliding
handle_gravity then accelerates the object downward if it isn't grounded
otherwise is gets the floor game object using context.functions.get_game_object, and then figures out the top edge using its position and size
it then set's the bottom edge of the object to be at the top edge of the floor
it then predicts the next frame to see if the player will hit the ground using context.functions.is_colliding_pos, and passing the position that will be predicted next frame. it it will, then it will snap the player to the ground again, else, apply gravity.
the get_forward function is defined and converts the z component of the rotation to radians, and then uses sin and cos to figure out the forward vector for that rotation and returns it.