[icon ] blenderdumbass . org [icon scene] Articles

Gamedev Makes You Better at Math

February 11, 2025

👁 129

https://blenderdumbass.org/ : 👁 10
https://blenderdumbass.org/articles : 👁 1
https://discuss.tchncs.de/ : 👁 1
https://mastodon.social/ : 👁 6
https://blenderdumbass.org/articles/the_incels_of_computing:_the_depressive_defense_mechanisms_of_free_software : 👁 1
https://mastodon.online/ : 👁 1
https://mementomori.social/ : 👁 1
https://blenderdumbass.org/articles/the_police-force_paradox : 👁 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




[avatar]by Blender Dumbass

Aka: J.Y. Amihud. A Jewish by blood, multifaceted artist with experience in film-making, visual effects, programming, game development, music and more. A philosopher at heart. An activist for freedom and privacy. Anti-Paternalist. A user of Libre Software. Speaking at least 3 human languages. The writer and director of the 2023 film "Moria's Race" and the lead developer of it's game sequel "Dani's Race".


10 Minute Read



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 unlike] 0
[icon left]
[icon right]
[icon terminal]
[icon markdown]

Find this post on Mastodon

[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 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 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 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 reply]
[icon question]











[icon articles]Gamedev Makes You Better at Math

[thumbnail]

[avatar]  Blender Dumbass

👁 129 💬 4



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


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


[icon articles]The Spaghetti Code Of Dani's Race

[thumbnail]

[avatar]  Blender Dumbass

👁 207 💬 1



Spaghetti code! The insidious thing that often happens even to the best of us. No wander that it happened to me. When programming you want to break your code into functions that could be called from many other places. Doing everything as one large function is a problem, because sometimes you might want to do the same operation or the same check, or whatever, again in another place, and that will require you to copy paste large chunks of code. And if suddenly you decide to change something about those checks, or functions, you have to change that something in all those places one after another. Dani's Race my game, has a bit of a Spaghetti code problem.


#DanisRace #MoriasRace #GameDev #FreeSoftware #Gnu #Linux #OpenSource #GtaClone #Programming #Python #SpaghettiCode #UPBGE #blender3d


[icon articles]A Rant About Making a Multiplayer Game

[thumbnail]

[avatar]  Blender Dumbass

👁 108 💬 1



The multiplayer, or the lack there of, at the moment is so utterly broken and so lacking of being properly made that for a long time, I was just not bothering with it. Seeing it as something unnecessary. Something that does not need to be touched, because other things, like the story or some gimmicky thing is more important for the game than the multiplayer. But people's demands for it didn't stop. So I thought now is a good time to actually properly design it.



#Gamedev #DanisRace #Networking #Multiplayer #TCP #HTTP #Programming #Python #UPBGE #Blender3d #GNU #Linux #GamingOnLinux #FreeSoftware #OpenSource


[icon articles]So I Cleaned Up Some Spaghetti from Dani's Race Code

[thumbnail]

[avatar]  Blender Dumbass

👁 149



A few days ago I had told you that I'm stopping with making the police station in my game Dani's Race because it had a Spaghetti Code Problem. The main() function in the Main_Update.py file was 1683 lines of code long and contained way too many things in it. At the moment, the same function is down to 641 lines of code. This is still way too much stuff in the main() but this is a hell of a lot of reduction.


#DanisRace #MoriasRace #GameDev #FreeSoftware #Gnu #Linux #OpenSource #GtaClone #Programming #Python #SpaghettiCode #UPBGE #blender3d


[icon articles]Dani's Race Franzo Livestream Report

[thumbnail]

[avatar]  Blender Dumbass

👁 51



Yesterday ( 5th October 2024 ) Franzo from opensource_gaming did a livestream of a game I am developing called Dani's Race.


#DanisRace #MoriasRace #GameDev #FreeSoftware #Gnu #Linux #OpenSource #GtaClone #Programming #Python #UPBGE #blender3d #Franzo #Livestream


[icon codeberg] Powered with BDServer [icon python] Plugins [icon theme] Themes [icon analytics] Analytics [icon email] Contact [icon mastodon] Mastodon
[icon unlock]