How to: crank powered moving platforms

Hi, I'm Kieran, also known as ReverendMalerik, and now that my game 'Ribbit Rabbit!' is finished and out in the wild (Ribbit Rabbit Special! For the Panic Playdate! by reverendmalerik) I thought it would be nice to break out a few of the features of the game that might be useful to other people and make them into little how-to guides.

This one is about the crank-powered moving platforms in my game. On certain levels in Ribbit Rabbit you can go to specific tiles, turn the crank and see all the platforms sat on rails on the level move according to which way you turned the crank. Here I will break down how I achieved this.

Step1: Get the crank to move the platform

This seems relatively straightforward, but there are in fact several parts to make it work the way we want it to. First up is this code, which is stored on the player's script:

on crank do
	if event.ra>0 then
		emit "crankup"
	else
		emit "crankdown"
	end
end

So as you can see, very simply, when you turn the crank one way, it emits 'crankup' and the other way it emits 'crankdown'.

I only want the crank to make the platforms move when the player is in a certain spot though, so a little modification is needed:

on crank do

        // check the name of the current tile
	tilename = name event.px,event.py

        // if it is 'crankbase' trigger the function, otherwise do nothing
	if tilename=="crankbase" then
		if event.ra>0 then
			emit "crankup"
		else
			emit "crankdown"
		end
	end
end

Now it will only fire if the crank is turned on the tile 'crankbase'.

However, if we were to use this to control the crank on its own it would result in the platforms flying from one end of the track to the other very quickly, so we need a function in-between to slow it down.

Hence we store the following code on the tile with the image of the crank, which we use as an in-between stage:

on crankup do

	// first we get the current frame
        framecounter = event.frame

        // we compare this to the stored framesince variable
	framecounter -= framesince

        // if it has been more than 5 frames since the last time we allowed the function to trigger, we let it trigger again
	if framecounter>5 then

                // play our crank noise
		sound "crankup"

                // store the current frame, so that we now use this frame as the point of comparison
		framesince = event.frame

                // check what frame of the tile is displaying, then switch to the other frame. This gives the impression that the crank is 'turning'
		thisframe = frame
		if thisframe==0 then
			frame 1
		else
			frame 0
		end

                // finally we emit 'chainup' which actually triggers the platform movement
		emit "chainup"

	end
end


// This second function does the same as the above, but triggers on 'crankdown' and emits 'chaindown' instead. It uses the same 'framesince' as above, however, because there is no need for a second copy of that.
on crankdown do
	framecounter = event.frame
	framecounter -= framesince
	if framecounter>5 then
		sound "crankdown"
		framesince = event.frame
		thisframe = frame
		if thisframe==0 then
			frame 1
		else
			frame 0
		end
		emit "chaindown"
	end
end

So here we have slowed down the platforms' movement and changed from using 'crankup' and 'crankdown' to 'chainup' and 'chaindown'. These are picked up by our tiles 'chainplat' (platforms that move up and down) and 'chainplatside' (platforms that move side to side). Both types of tile move along tiles named 'chain', but stop when hitting any other type of tile. This is achieved using the following code, which is stored on the 'chainplat' tile (with an equivalent on the 'chainplatside' tile):

on chainup do
        // get the event x and y values
	myx = event.x
	myy = event.y

        // subtract 1 from the y value
	myy--

        // myx and myy now give the co-ordinates of the tile above this one. We check what kind of tile it is
	whattile = name myx,myy

        // if it is chain, then we move the platform using the following, otherwise we do nothing.
	if whattile=="chain" then

                // we change this tile to chain
		swap "chain"
               
                // we change the tile above, that we verified as chain, into the platform instead, "moving" the platform up one.
		tell myx,myy to
			swap "chainplat"
		end
	end
end

// This function performs the same purpose, but for moving the platform down.
on chaindown do
	myx = event.x
	myy = event.y
	myy++
	whattile = name myx,myy
	if whattile=="chain" then
		swap "chain"
		tell myx,myy to
	                swap "chainplat"
                end
	end
end

The 'chainplatside' tile has the equivalent functions for moving from side to side, altering the 'myx' value instead of the 'myy'.

And there you go! You've got moving platforms in your pulp game!

Step 2: Multiple platforms

But what if you wanted to have multiple platforms on each track? Would that work? A quick test proves that yes it does! However, there is one strange exception.

When two platforms next to each other move either left or up, they move that direction together. However, when two platforms are together and move either down or right, only the platform closest to the bottom or the right will move, with the other remaining where it is.

Obviously this is not ideal. First of all, to explain what is happening, it works like this. When the emit function is run, it triggers on all tiles on the screen, but not simultaneously. It runs from top left to bottom right. This causes a problem for us, because when the top/left platform runs its chaindown function, it checks the platform below it/to the right and, as it is a platform and not chain, it does not move. Then the bottom/right platform runs its chaindown function and finds chain, causing it to move.

So! How do we make it so that BOTH platforms move? Or, if there are more than two platforms, they ALL move?

Well, this took me a lot of experimentation, but here is the solution I came up with. First of all, we need to edit the initial crank event:

on crank do
	tilename = name event.px,event.py
	if tilename=="crankbase" then
		if event.ra>0 then
			emit "crankup"
		else
			emit "crankdown"

			// we need to trigger a second event, which we trigger here.
			emit "crankdown2"
		end
	end
end

With that out of the way, we need to modify the chaindown event on the platform tiles. Below is the one for the up and down platform, but the left to right version is altered in a very similar way, just using the x axis instead of the y.

// This works fine, so no alteration is needed
on chainup do
	myx = event.x
	myy = event.y
	myy--
	whattile = name myx,myy
	if whattile=="chain" then
		swap "chain"
		tell myx,myy to
			swap "chainplat"
		end
	end
end

on chaindown do

	// as before, this checks the tile below

	myx = event.x
	myy = event.y
	myy++
	whattile = name myx,myy

	// however now we also check the tile ABOVE

	log "whattile {whattile} - {myx},{myy}"
	myx2 = event.x
	myy2 = event.y
	myy2--
	whattile2 = name myx2,myy2

	// if the tile below us is 'chain', we continue as before but with some changes

	if whattile=="chain" then

		// we check if the tile above us is 'chainplat'. If it is, instead of changing the 'chain' tile to a platform, we change it to 'holdingtile2' for the time being

		if whattile2=="chainplat" then
			tell myx,myy to
				swap "holdingtile2"
			end

		// if the tile behind us is 'holdingtile1' then we do the same

		elseif whattile2=="holdingtile1" then
			tell myx,myy to
				swap "holdingtile2"
			end

		// if the tile behind us is chain, then we do the same as well.

		else
			swap "chain"
			tell myx,myy to
				swap "holdingtile2"
			end
		end

	// if the tile below us is a platform ('chainplat') then we continue as follows

	elseif whattile=="chainplat" then


		// if the tile above us is also a platform, we don't need to do anything, as the correct result is for this square to end with a platform on it.

		if whattile2=="chainplat" then
			// do nothing

		// if the tile above us is 'holdingtile1' then also do nothing

		elseif whattile2=="holdingtile1" then
			// do nothing

		// otherwise change this tile into 'holdingtile1'

		else
			swap "holdingtile1"
		end
	else
		bouncex = event.x
		bouncey = event.y
		bouncey--
		okay = "yes"
		while okay=="yes" do
			call "checkifempty"
			bouncey--
		end
	end
end

on checkifempty do
	thetile = name bouncex,bouncey
	if thetile=="chainplat" then
	elseif thetile=="holdingtile1" then
		tell bouncex,bouncey to
			swap "chainplat"
		end
	else
		okay = "no"
	end
end

So now, after this has finished, we are left with a lot of 'holdingtile1' and 'holdingtile2' tiles where 'chain' and 'chainplat' tiles should be, respectively. This is where the alteration we made to the initial 'crank' event comes in, triggering 'crankdown2', which is an event on the 'holdingtile1' and 'holdingtile2' tiles as follows:

'holdingtile1':

on crankdown2 do
	swap "chain"
end

'holdingtile2':

on crankdown2 do
	swap "chainplat"
end

Nice and simple, it turns the tiles into their respective counterparts.

And there you have it, a fully working system to move your platforms around using the crank! I hope someone finds it useful! If you have any questions please feel free to ask and I will try my best to answer.

As I stated at the top I will be posting more tutorials based on features from my game 'Ribbit Rabbit!' as I get time. In the meantime, please check it out on itch (Ribbit Rabbit Special! For the Panic Playdate! by reverendmalerik) and try the free 4 level demo!

2 Likes