[icon ] blenderdumbass . org [icon scene] Articles

Gamedev Makes You Better at Math

[avatar]  Blender Dumbass

February 11, 2025

👁 56

https://blenderdumbass.org/ : 👁 9
https://blenderdumbass.org/articles : 👁 1

#Gamedev #DanisRace #Math #Programming #Python #UPBGE #Blender3d #GNU #Linux #GamingOnLinux #FreeSoftware #OpenSource

License:
Creative Commons Attribution Share-Alike
Not AI Generated! [icon internet] See Proof
Audio Version





A small reminder that the petition for Dani's Race version 2025-01-19 is still in full swing. So please sign it to help the game.


When I started with Dani's Race I thought that the extend of my programming will be something like dynamically changing the speed of a car and maybe loading and unloading certain things on the map based on the distance from them. But quickly it became a mind-field of math and other programming cleverness. About which I will talk to you in this article.





Finding Distances


UPBGE, being based on Blender Game Engine, has a rather simple way to find a distance between two objects.

distance = objectA.getDistanceTo(objectB)


Even if I was using some other game engine, your typical math module in python or similar languages should include a distance function of some kind. Just you will need to use the position of the object instead of the object itself. In UPBGE I can do this as well:

import math
distance = math.dist(objectA.position, objectB.position)


This is fine. But sometimes this is not enough.


[embedded image]


Take for example this problem. I want to draw a lot of trees and a lot of other destructible objects all around the city in the game. Actually as of now, the game has 994 trees, 619 light-posts, 675 metal gates and 576 wooden gates. It is 2864 objects. Yet I want everything to run on a descent speed, so that the game is playable.

What I could do is, say hide objects that are farther than a specific draw distance. And spawn them back in when you get close to the point of where they are. Not too complex.

drawDistance = 250
for object in scene.objects:
    if object.getDistanceTo(scene.active_camera) < drawDistance:
        Spawn(object)
    else:
        Despawn(object)



But this will make 2864 distance calculations on every frame. On my beefy CPU calculating 2864 math.dist calls takes about 18 milliseconds. Which is not a lot. But for 60 FPS you want to have the whole thing ( all calculations of all logic and all rendering ) happen in under 16 milliseconds. So just calculating distances to all those objects, to either hide them or un-hide them takes way too much time. And I'm not even talking about the penalty of constantly hiding and un-hiding them. So there must be a better way.

Maybe if I reduce the amount of objects? Maybe if I make say every 10 trees on the map be one object in a technical sense, which just so happens to depict 10 trees. I've thought about all of those possible solutions. I considered making all those trees be one chunk, so to speak. But then I will have to hold in memory many different configurations of trees for every part of the map. And that could become very resource intensive really quick too.

Instead I decided to math my way out of it.

Python has this thing called Dictionaries. Which is like a list, but from where you can get things very quickly.

# Here I'm making a list of trees with their names.
# Using lists in python.

objects = [
    ["Tree1", objectOfTree()],
    ["Tree2", objectOfTree()]
]

# And here I want to find Tree2

Tree2 = None
for object in objects:
    if object[0] == "Tree2":
        Tree2 = object[1]
        break



Now compare that to doing this instead.

# Here I'm doing the same thing but with a dictionary.

objects = {
    "Tree1":objectOfTree(),
    "Tree2":objectOfTree()
}

# And I'm getting Tree2

Tree2 = objcets.get("Tree2", None)



This will find the object you need much faster, like in orders of magnitudes faster, when you are talking about hundreds of objects, and is even cleaner as a piece of code. But anyway. Say I would group every 10 trees into a dictionary like this. How would I do that? The dictionary has a key, which is a name of sorts, and I need to know what that key is, to get the list of the trees. How can I make that key represent the distance to the camera?


[embedded image]


So here is what I came up with. I can separate everything into neighborhoods. Square patches of the map. And then load the trees that are from the same square as the camera.

I use a very basic, very quick math calculation to find which square any point in 3D space is in.

def Address(location, precision):
    
    ret = ""
    for axis in location[:2]:
        ret = ret + str(round(axis/precision))+":"
    return ret



Which, as you can see in the code, basically just divides the position coordinated X and Y by a certain precision value ( the size of the square ) and then stitches those numbers together into a string like this 1:1: or 20:-30: or -15:0:, or something.

Those codes I can then use as keys for the dictionary that is referring to all of the objects in a given square cell. So when I call chunks.get("1:1:", I get all of the objects who's address is 1:1:. Of course the objects needs to be placed in said dictionary beforehand. Luckily it could be done only once. And therefor it is done only once, on the first frame of the game.


[embedded image]


Now this is brilliant and all. But it does have an issue. What if the player is at an edge of one square looking toward a direction of a different square. If only the objects in the square where the player is will load, he will see lack of detail ahead of him. And that is not good.


[embedded image]


I guess, if I make it read not only the address of the square the camera is in, but also roughly where the camera is looking at, I could give the player a more full picture. For that sampling a few more points: few in front of the camera, and a few diagonally, from the camera, should not make it too slow. Especially if I make it load only one chunk at a time.

The problem now is, how do I get those points?





Doing geometry and stuff


I know I could have parented something to the camera and sample the locations of those somethings. But if you remember the issue with depsgraph, I'm trying to stay away from having too many objects. So whenever possible I should use math directly to find specific points.

Luckily blender comes with a very good python module ( that I want python to have by default ) called mathutils. This module is designed to help calculate geometrical things. Vectors, rotations, color stuff and other things that are vector-like.

So to calculate a point which supposed to be, say 250 meters in front of the camera I can do something like this.

import mathutils
cam = scene.active_camera
point = mathutils.Vector((0,0,-250))
point.rotate(cam.orientation.to_euler())
point += cam.position



Yup. Unlike lists of coordinates, mathutils.Vector() object can be rotated by a rotation object ( which if you are using euler is just a list of XYZ orientation changes in radians ). And also you can add any of them on the fly. So I can make a point which sits at 0 and 0 on X and Y and -250 on Z ( which is 250 in front of the camera if the camera was at the center of the 3D world and rotated to 0,0,0 ), I can then rotate that point to the rotation of the camera. Which will move the point in space to where this point would be if such rotation would be taken place. And then I can move it to where the camera is. And guess what. I will suddenly be in-front of the camera, 250 meters away.

ret = []
points = [
    mathutils.Vector((0,0,-1)),
    mathutils.Vector((0,0,0)),
    mathutils.Vector((0.5,0,-0.5)),
    mathutils.Vector((-0.5,0,-0.5)),
    mathutils.Vector((0,0,-2))
]
for point in points:
    point *= precision
    point.rotate(camera)
    point += location
    ret.append(Address(point, precision))
	


So now using a few lines of code that define a few points. I can get the list of all addresses I need to look at when trying to decide which Trees to show and which to hide. But more than that. I can use the math in many different places too.


[embedded image]


For example: To map things on the mini-map in the corner of the screen. Yeah those are little objects that move to positions relative to where the mini-map is in the 3D world ( which is parented to the camera ).


[embedded image]


Or to test whether the car is on the ground or not. Where a ray is shined onto a relative point towards with bottom the car. Or to know whether Dani is grabbing an edge of something to climb it. Which is done with two rays, one from the stomach and one from the head. Each requiring a relative point.

One thing leads to another. One discovery to the next. And that is how Gamedev makes you better at math.

Happy Hacking!!!


And again the petition for Dani's Race version 2025-01-19 is still in full swing. So please sign it to help the game.


[icon question] Help





Subscribe RSS
[icon link] Author
[icon link] Website
Share on Mastodon


[icon question] Help








[avatar]  Anonymous Guest c:0


2pi is not 180 degrees, rather 360, due to pi being equal to 180!

... replies ( 1 )
[avatar]  Blender Dumbass c:2



@Anonymous Guest Patched, lol. Thank you for noticing.




[icon send] Reply
[avatar]  Anonymous Guest c:1


What was written is true, if a game developer such as Freya Holmér can, without any academic background, dig into the weeds of vectors, then any dev can, willing he is to embrace the unknown.

[icon send] Reply
[avatar]  Blender Dumbass c:2


... c:0
[avatar]  Anonymous Guest c:0


2pi is not 180 degrees, rather 360, due to pi being equal to 180!


@Anonymous Guest Patched, lol. Thank you for noticing.

... replies ( 1 )
[avatar]  ANONYMOUS c:3



@blenderdumbass

Did you?




[icon send] Reply
[avatar]  ANONYMOUS c:3


... c:2
[avatar]  Blender Dumbass c:2


c:0

@Anonymous Guest Patched, lol. Thank you for noticing.


@blenderdumbass

Did you?

[icon send] Reply



[icon malware]Interference

  Unread  

[thumbnail]

[avatar]  Blender Dumbass

👁 24 💬 1



Sometimes the developers just include into their software something that is there not the user's sake. Which ends up Interfering with user's needs. And it's just plain annoying.



[icon petitions]Release: Dani's Race v2025-01-19

  Unread  

[thumbnail]


240 / 250 Signatures

[avatar]  Blender Dumbass

👁 101 💬 0



Dani's Race version 2025-01-19


#DanisRace #MoriasRace #Game #UPBGE #blender3d #project #petition #release


[icon reviews]What Lies Beneath

  Unread  

[thumbnail]

[avatar]  Blender Dumbass

👁 32 💬 0



Have you ever wondered what would Alfred Hitchcock do in the age of CGI and VFX? What kind of strange insane shorts he would come up with? Well Robert Zemeckis set out for himself a challenge to find out. He is notorious for using visual effects creatively. A lot of people might be familiar with the mirror shot he did in the film Contact. So something like trying to make a Hitchcockian thriller of the 21st century was just about the right kind of thing for Zemeckis.


[icon articles]UPBGE - What is Depsgraph? And How to Optimize for Depsgraph?

  Unread  

[thumbnail]

[avatar]  Blender Dumbass

👁 86 💬 0



You see things like "Physics", "Logic" and even "Rasterizer" and you immediately understand what you need to do to optimize you game. But "Depsgraph"?... It looks like a mysterious thing that nobody knows nothing about. Yet is it one the most problematic things there is in your game. And you are going mad just trying to figure it out.



#DanisRace #MoriasRace #Game #Gamedev #UPBGE #blender3d #animation #GTAClone #programming #python #project #performance #depsgraph


[icon codeberg] Powered with BDServer [icon analytics] Analytics [icon mastodon] Mastodon [icon peertube] PeerTube [icon element] Matrix
[icon user] Login