How to do many things at once

From MiniScript Wiki
Revision as of 03:36, 3 June 2020 by JoeStrout (talk | contribs) (fixed typo)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

MiniScript can only do one thing at a time, but by switching between many tasks very quickly, it can create the illusion of doing many things at once. (This is in fact how all computers worked before the advent of multi-processor machines, and even today it is a common technique used in game engines such as Unity.)

The standard way to do this is to have a main loop that runs the whole time your program is running, which does nothing but call other functions to update various things (move sprites, play or stop sounds, do calculations, etc.). The main loop should call yield to give the computer a bit of a rest on each frame, and give you a consistent program speed; this will make your main loop run 60 times per second unless your update methods take longer than about 16 milliseconds.

As an extra feature, it may be useful to calculate the exact time step (often called dt) between iterations of the main loop, and provide this value to your update methods. They can use this to move things further when the time step is long (for example, because the computer is getting bogged down, or it's running on a slower machine or web browser). That will give the motion a consistent speed regardless of frame rate.

Example

Let's prepare a sprite, and define an update method that does a simple physics simulation to make the sprite bounce.

ball = new Sprite
ball.image = file.loadImage("/sys/pics/XO/O-blue.png")
ball.x = 480
ball.y = 600
ball.vy = 0
display(4).sprites.push ball
ball.update = function(dt)
	self.vy = self.vy - 1000 * dt
	self.y = self.y + self.vy * dt
	if self.y < 100 then self.vy = abs(self.vy)
end function

Notice that the ball.update function takes a dt (time step) parameter. It uses this to calculate how much the ball's vertical speed (vy) changes due to gravity, and then uses it again to apply this speed to the ball's position.

Now let's make another update function, that simply draws a counter based on the time value on the text display.

updateCounter = function(dt)
	counter = round(time)
	if counter == prevCounter then return
	text.row = 25
	print counter
	globals.prevCounter = counter
end function

This function doesn't use the dt parameter, but it's defined to accept it anyway. This is convenient when you have a lot of things to update, especially if they are methods on an object, since your main loop can then zip over all such objects and update them all in the same way.

But in this case we have only two things to update; let's make a main loop that updates them.

lastTime = time
while not key.pressed("escape")
	now = time
	dt = now - lastTime
	lastTime = now
	updateCounter dt   // Update the counter
	ball.update dt     // Update the ball
	yield
end while

Each time through the main loop, we grab the current time value, subtract the last time from it to calculate the time step dt, and then store the current time as lastTime for our next iteration. Then we pass the dt value to each of our update methods. Finally, remember to yield before the end of the loop.

Complete Program

Paste this into Mini Micro to see it in action.

clear
prevCounter = ""
updateCounter = function(dt)
	counter = round(time)
	if counter == prevCounter then return
	text.row = 25
	print counter
	globals.prevCounter = counter
end function

ball = new Sprite
ball.image = file.loadImage("/sys/pics/XO/O-blue.png")
ball.x = 480
ball.y = 600
ball.vy = 0
display(4).sprites.push ball
ball.update = function(dt)
	self.vy = self.vy - 1000 * dt
	self.y = self.y + self.vy * dt
	if self.y < 100 then self.vy = abs(self.vy)
end function

// Main loop:
lastTime = time
while not key.pressed("escape")
	now = time
	dt = now - lastTime
	lastTime = now
	updateCounter dt   // Update the counter
	ball.update dt     // Update the ball
	yield
end while

Once you get in the habit of using this pattern, it becomes really easy to do even more things at once: just define an appropriate update method, and call it from the main loop.

More Examples

Many of the demos in /sys/demo/ use this pattern, including balloons, drumMachine, flappyBat, inputCheck, mochiBounce, platformer, speedConquest, theMatrix, typing, and wumpusTrap.