![Depsgraph in framerate and profile [embedded image]](/pictures/despgraph/1.png)
![Changing color on the fly. [embedded image]](/pictures/despgraph/2.png)
![The deepness of Color in blender. [embedded image]](/pictures/despgraph/3.png)
![A lot of objects. [embedded image]](/pictures/despgraph/4.jpg)
Object.position = (0,0,0)
applyMovement() function. So I made a simple function like this:
↩ Reply
import mathutils def Move(object, position, times=1): # Moving object using applyMovement object.applyMovement( ( mathutils.Vector(position) - object.position ) * times )
applyMovement() function.
↩ Reply
def Delete(object, inactive=False): # To make this callable from logic bricks the next thing is needed. try: object = object.owner except: pass # Making a list of those objects in reuse dictionary. # Technically if Create() was use to create this object it's # not needed, but incase other objects will be stored like this. if object.name not in reuse: reuse[object.name] = [] # Sometimes just recording that the object is available is enough: if not inactive: # Instead of deleting we are going to store it for later object.worldLinearVelocity = [0,0,0] object.worldAngularVelocity = [0,0,0] object.scaling = [1,1,1] object.suspendPhysics() Move(object, (0,0,-1000)) object.visible = False # For some objects if "Motion" in object.actuators: object.actuators["Motion"].dLoc = [0,0,0] # Storing the object for later use if object not in reuse[object.name]: reuse[object.name].append(object) # Making sure it will not self distract again after it is reused object["self-destruct"] = 2 if object in selfDestruct: selfDestruct.remove(object)
object.endObject() present anywhere. The object is not actually being removed from the scene. But instead as you can see it is being moved to coordinates of 0, 0, -1000, it is being made invisible and the physics of the object are being suspended.
↩ Reply
def Create(object, selfDestructFrames=0, selfDestructInactive=False, visible=True, declarenew=False, frompoint=None): # Making a list of those objects in reuse dictionary. if object not in reuse: reuse[object] = [] # If the list is empty ( we need more objects ) we make a new one. if not reuse[object]: if not frompoint: frompoint = object object = scene.addObject(object, frompoint, 0, False) new = True if object.name not in amounts: amounts[object.name] = 0 amounts[object.name] += 1 else: object = reuse[object].pop(0) object.restorePhysics() object.worldLinearVelocity = [0,0,0] object.worldAngularVelocity = [0,0,0] new = False # If self descructing if selfDestructFrames: SelfDestruct(object, selfDestructFrames, selfDestructInactive) object.setVisible( visible ) if declarenew: return object, new else: return object
scene.addObject() function, but only for those objects that were not yet deleted before. But if there is a deleted object that is available to be used, it will re-enable its physics, make it visible again, and use it.
↩ Reply
Init() function about which I talked not so long ago, in a different article there is a whole thing dedicated to precalculating of those objects. Basically I know that roughly speaking 50 Light Poles might be visible as some point in the game at once. And all of them will need to be spawned into position when the character arrives to the place where they spawn.
↩ Reply
![A lot of light poles. [embedded image]](/pictures/despgraph/5.jpg)
GoodFPS().
↩ Reply
impact = {"test":None, "lastTestTime":0, "data":{}, "frame":0, "timer":1, "estimate":None} def CalculateImpact(previous, current): result = {} for i in previous: p = previous[i][0] c = current [i][0] result[i] = c - p return result def GoodFPS(func="", factor=1.0, boolean=True, traceback=False ): # This will be used in an if statement before executing # some, potentially intense work. And if the performance # is dropped below a certain amount. It will stop executing # that command. settings = bge.logic.globalDict.get("settings", {}) frame = round(bge.logic.getRealTime(), 2) currentMetrics = bge.logic.getProfileInfo() targetFPS = settings.get("fps", 30) if not boolean: return bge.logic.getAverageFrameRate() / targetFPS # We need to let the game initialize for a few seconds. # So we limit all executions in that time. if frame < 5: return 0 # Controlling the amount of how aggressively this function is executed. optset = settings.get("opt", 1.0) ** 0.1 choice = numpy.random.choice([False, True], p=[optset, 1-optset]) if choice: # Restraining over-bombardment. if func in impact["data"]: if impact["data"][func].get("lastframe",0) + impact["timer"] > frame: return 0.0 return 1.0 # Calculating impact timer ( which will time how often one function can be # exectuted ). impact["timer"] = 10 - (10 * ( bge.logic.getAverageFrameRate() / targetFPS )) # Recalculating if FPS goes too low. # TODO: figure out a way to actually do that reasonably. #if bge.logic.getAverageFrameRate() < targetFPS * 0.5: # impact["data"] = {} # We want to record FPS impacts of various executed things. # 1 | On first 2 frames it should tell everyone to do nothing. # 2 | On second frame it should record profile. And execute. # 3 | Then on third comparing the profiles should give us impact. if not impact["test"] and func not in impact["data"] and \ impact["lastTestTime"] + impact["timer"] < frame: impact["test"] = { "func" :func, "stage" :0, "frame" :frame, "profile":None } # This is needed to not bombard the player with slow tests # immediately. impact["lastTestTime"] = frame if impact["test"]: test = impact["test"] if test["stage"] == 0 and test["frame"] < frame: test["frame"] = frame test["stage"] = 1 if test["stage"] == 1: test["frame"] = frame test["profile"] = currentMetrics.copy() test["stage"] = 2 return 1.0 if test["stage"] == 2 and test["frame"] < frame: impactIs = CalculateImpact(test["profile"], currentMetrics) impact["test"] = None impact["data"][func] = impactIs return 0.0 elif func in impact["data"]: # If we have data about how agressive the changes are, # we can use that data to estimate next frames FPS with it. # If the function was recently activate, skip it. if impact["data"][func].get("lastframe",0) + impact["timer"] > frame: return 0.0 # Clearing out the estimates on new frames if impact["frame"] != frame: impact["frame"] = frame impact["estimate"] = None # Making a fresh estimate if not impact["estimate"]: impact["estimate"] = {} for i in currentMetrics: impact["estimate"][i] = currentMetrics[i][0] # Creating a temporary estimate only for this function tempEstimate = impact["estimate"].copy() # Calculating it's function impact totalMS = 0 for i in tempEstimate: tempEstimate[i] += impact["data"][func][i] totalMS += tempEstimate[i] # Deciding whether we have speed to spare on this calculation targetMS = 1 / ( ( targetFPS ) / 1000 ) if totalMS > targetMS: return 0.0 else: impact["estimate"] = tempEstimate impact["data"][func]["lastframe"] = frame return 1.0
Vehicle.Spawn("NeonSpeedsterBox", (0,0,0), (0,0,0))
if Opt.GoodFPS("Spawning an NPC Car"): Vehicle.Spawn("NeonSpeedsterBox", (0,0,0), (0,0,0))
GoodFPS() function will encounter the "Spawning an NPC Car" call, it will suspend everything else, returning False everywhere for a few frames, while returning True ones for this call. This will activate it and the GoodFPS() function will be able to record the impact this call has on the profile. Not just on the overall FPS, but on every individual category: Logic, Animation, Physics, Depsgraph and so on, separately.
↩ Reply
![Screenshot of the game. [embedded image]](https://files.mastodon.online/media_attachments/files/113/757/734/695/021/413/original/19c4506958fb7ab0.png)
![[thumbnail]](https://forg.madiator.cloud/BlenderDumbass/DanisRace/raw/branch/main/SettingUI/banner.jpg)