'moving' multiple potentially contiguous sprites

The crank drives cars forward along the x axis (and backward, but keeping it simple for this example):
a] swap the next tile forward to the car
b] make the current tile whatever the previous tile was.
So far, so good with one car.

And multiple cars… as long as there is at least one road tile between cars (ie, there are not two cars directly in a row). Here's my simplified code:

[player script]
on crank do
	if event.ra>0 then
		emit "drive_forward"
	end
end
[car script]
on drive_forward do
	next_x = event.x
	next_x++ // locate the tile in front of the car
	last_x = event.x
	last_x-- // locate the tile behind the car
	lastTile = name last_x,event.y // get that previous tile name
	tell next_x,event.y to 
		swap "car" // move the car up a tile
	end
	tell event.x,event.y to
		swap "{lastTile}" // swap in the previous tile where the car was
	end
end

If it matters, I've got the cars as sprites. (I've got a separate player.)

I think the problem relates to the iteration of the script replacing road tiles all the way up until the lead car in a chain. If that's right, is there a better way to do this? (If that's wrong, what is happening?)

What's the problem you're seeing? :slightly_smiling_face:

Ha, I was trying to carefully explain the set-up and forgot to explain the issue. Let's see if I can diagram it.

This works perfectly:

[_][X][_][_][_][X][_][_][_] (crank forward)
[_][_][X][_][_][_][X][_][_] (crank forward)
[_][_][_][X][_][_][_][X][_]

but if I have 2+ cars in a row, I get:

[_][X][X][X][_][X][X][_][_] (crank forward)
[_][_][_][_][X][_][_][X][_] (crank forward)
[_][_][_][_][_][X][_][_][X]

The code swaps road tiles over all but the lead car if there are multiple car tiles without a road tile between.

Well, one problem I can see is, that when a car B is directly behind a car A, this part…

tell event.x,event.y to
	swap "{lastTile}" // swap in the previous tile where the car was
end

… already moves the car B behind car A. And after the script for car A is finished, there are three cars left: car A, that moved on, the swapped in car B.swap at the old position of car A, and car B, which wasn’t deleted!

As a little graphic:
Before: X X B A X X
After: X X B B.swap A X X

I’d fix this first and see, if the problem is still there or changed. I guess you could do that by checking, whether the tile behind car A is also a car. If yes, just move car A forward and swap in a street tile.

Hi @daverutledge - I have a little experience with complicated swap routines via the endless runner I'm currently porting over from desktop.

Would it make more sense to swap the current car position with the tile in front of the car, rather than locating the tile behind the car's current position? That would limit this to a two tile equation, rather than a three tile equation (like "leap frog").

Thinking it would be something more along these lines:

on drive_forward do
	next_x = event.x
	next_x++ // locate the tile in front of the car
	lastTile = name next_x,event.y // get the name of the tile currently in front of the car
	tell next_x,event.y to 
		swap "car" // move the car up a tile
	end
	tell event.x,event.y to
		swap "{lastTile}" // swap in the previous tile where the car was
	end
end

You'll also want to handle the cases where event.x == 24, but the exact approach will depend on your game design.

@Bro-Code I've now tried both approaches and…I'm even more confused.
a) moving forward, the 3-tile approach (my original idea) kills off all but the lead vehicle
b) moving forward, the 2-tile approach (your suggestion) spaces out adjacent vehicles
c) moving backward, everything works great until the wrap-around and then each break in different ways

The difference between driving forward and backward is making me think this may be a quirk of how 'emit' cycles through sprites. Oh, and to be clear, the wrap-around (backward and forward) works fine when there's only one car in a row.

I created a "game" that's my attempt to simplify this case as well as allow me to try multiple variants. I attached the .json here in case anyone's up for seeing if/where I went wrong and/or coming up with another approach. For further simplicity, I've removed the crank aspect, and the A button moves forward and the B button moves back. I recommend loading it, and moving backward one button-press at a time until it breaks. Then trying the same thing moving forward one button-press at a time.

It's entirely likely I've just coded this terribly and there will be an obvious fix, but I've simplified so much and still can't find any clear error. It's more likely there's just a much easier way to accomplish this. (For complicated reasons, I can't use 'player' for the vehicles.)

Here's the .json, if anyone is up for looking into this, feel free to ask me for more clarification or code.
Hello Cars.json.zip (3.0 KB)

@daverutledge I think I cracked the case.

First, I completely misunderstood the goal here. I was thinking it was a racing game and you wanted to move the player car in front of the others as the player progressed (static player, moving background). I now see what you're trying to accomplish.

The sprite recursion moves left to right, so when you are moving vehicles forward, you have to remember where the last car/truck was. Otherwise, if you have a string of multiple cars/trucks, the last one analyzed gets overwritten by a road tile and will be 'lost' in the subsequent iteration.

Similarly, if you have cars/trucks that are separated by only a single road tile, you can use the last truck's/car's location to know if you need to insert a road tile or not to maintain that single-tile separation.

There was also a bug when moving backwards if you had a string of cars/trucks together, there was no 'memory' of the first car/truck in the line (x == 0), so by the time you reached x==24 you were swapping in the already replaced tile rather than the original tile at the start of the movement.

There are a few new variables to track theses, and they need to be reset before each drive_forward and drive_backward routine, but you'll see the corrected code in the truck's script. I reset the car's script back to the three-position model so you can track the changes.
Hello Cars_Edit.json.zip (3.1 KB)

1 Like

@Bro-Code Ah ha! I feel like I was simultaneously very close and a big step away from getting this right. Setting a variable for the x of the last truck makes a lot of sense.

Thanks for both the coding help and the thorough explanation.

--
I do have a quirky situation I'm having trouble isolating. If I place a truck on 0,13 and then drive backwards, I get a stream of 4 trucks (rather than the expected 1). I'll have to stew on the code a bit more to see where/why that's happening.

Unfortunately, since the recursion appears to move left to right (from x==0 to x==24), if you start putting the same vehicle on multiple rows (Y values), you'll have to track a unique lastTruckX and lastCarX value for each row, e.g. lastTruckX_0, lastTruckX_1, lastTruckX_2...lastTruckX_24. You'd then have to compare against the event.y value to determine which row you were on and which lastTruckX_# value to use.

Another approach might be to just iterate through the entire collection of columns/rows and move each one left/right depending on the input command. This would allow you to start from either end of the y-series (0 or 24) based on the direction of movement, and you'd only have to capture the 'first' column tiles for each valid row at the start. This would give you one master movement event that could be run in the room rather than dispersing your code across each vehicle tile.

Yet another option could be to use frames to track the tile movement. If you created 3 replica frames for each tile type (road, truck, car), with an FPS of 0, you could let the frame position represent the previous tile type (0 = road, 1 = truck, 2 = car). This gives you a per tile variable to know what it was prior to being changed during the recursive tile forward/backward movement event execution. This actually might be the cleanest solution overall. I'm wrapping up for the night, but can help mock this up in the AM if you're still working it.

1 Like

Frame as variable is clever! However, in my specific case, I'm already using a unique vehicle sprite in each row. I had been mimicking the script from the first vehicle across my six sprites, so they were using the same lastTruckX variable. It seems like adding a unique variable will be my easiest solution. It’s not a huge deal if I have to copy/paste/tweak for each row.

[edit later] I hesitate to declare victory, but on implementing a lastTruck_# and zeroTruck_# variable in player for each row (and using distinct sprites per row), everything seems to be working on every variation of car spacing I have tried. There's probably some refactoring I can do to simplify, and I may build out a unit test that randomly places cars, drives them back and forth random amounts, and then counts how many remain to be sure, but it's looking good so far.

Thanks for all your help here!

1 Like