Is there a way to make a fast counter out of a TextNode?

As you probably know, updating a text node every frame is a huge performance drag. This is most annoying in counters (points, time, distance, etc.), which have to be updated quickly.

Since there are only 10 digits, it would be possible to load the geoms for each digit once, and show/hide them as necessary to avoid generating a new text node with each update. It seems like someone must done this already, but I’m not sure exactly what to search for. Does anyone have a snippet for this? If not, I’ll hack something together and post it.

Hi! :slight_smile:

I don’t know if this is what you are trying to achieve, but here’s a code snippet I’ve made for a “slow typing” effect.

This doesn’t generate a new text node, just modify it following some instructions via tasks.

Let me know if that was useful for you. :slight_smile: :slight_smile:

How about generating all the text (or a big part of it) and putting it on a small DirectScrolledFrame that shows just one line at a time. To update the text you’d just to scroll to the line you need.

For very rapid changes like a milisecond timer for a sport/race game you could make a hoax timer. Make a animated texture with some numbers and play it really fast, the player will only see a timer no matter what it shows, it’s just too fast.

Thanks for the info. The frame rate meter ( http://www.panda3d.org/reference/devel/python/classpanda3d.core.FrameRateMeter.php ) already does what I want, so there might be a way to modify that as well. Stay tuned…

OK, here’s my solution. I create all the text nodes in advance, and show or hide them as necessary to represent a given value.

In this case, I have a node with 50 children, 5 decimal places × 10 possible values. I can increment it every frame with negligible CPU usage.

import sys
from direct.showbase.DirectObject import DirectObject
from direct.directbase import DirectStart
from direct.task import Task
from panda3d.core import *

import assets

class Counter():
	def __init__(self, nodePath):
		self.nodePath=nodePath.attachNewNode('counter')
		self.nodePath.setScale(.1)
		
		self.value=0	#Number stored by the counter
		numPlaces=5	#Decimal places (Up to a value of 99999 in this case)
		self.places=[]
		
		#For each decimal place, add 10 text nodes, one for each digit.
		for i in range(numPlaces):
			digits=[]
			self.places.append(digits)
			
			for j in range(10):
				node=TextNode(str(j))
				node.setText(str(j))
				nodePath=self.nodePath.attachNewNode(node)
				nodePath.setX(-i) #Place nodes backwards, because decimal places go from right to left
				digits.append(nodePath)
		
	
	def setValue(self, value):
		#Don't update if there's no change in value
		if self.value != value:
			self.value=value
			
			#Loop through each decimal place.  Find value of that place, show the node corresponding
			#to it, and hide the others.
			for i in range(len(self.places)):
				
				placeValue=(value/(10**i)) % 10	
				digits=self.places[i]
				
				for j in range(len(digits)):
					digit = digits[j]
					if j != placeValue: digit.hide()
					else: digit.show()
	

if __name__=='__main__':
	counter=Counter(aspect2d)
	
	#Test the counter by incrementing its value every frame
	def testCounter(task):
		counter.setValue(counter.value+1)
		return task.cont
		
	base.taskMgr.add(testCounter, 'update value')

	base.accept('escape', sys.exit)
	
	run()