In the previous article in this series, we spent time setting up our game so that we had all of the necessary images in place, and randomly spawning a grid of blocks for the player. With those systems in place, we are now going to work on basic functionality and giving the player the ability to manipulate the game field.
Final Game Demo
Here is a demo of the game we're working towards throughout this series:
1. Adding Animation
We ended the previous tutorial after making every block display as a different color. I thought it would be good to pick up from that exact spot and begin by adding animation to the blocks so that the images change based on what the player does or did. The first animation we will add is a mouse-over event.
First, add a mouse object to Layout 1. Then, go back to Event Sheet 1 and add a new Event.
Event: Mouse>Cursor is over object Object = Block Action: Block>Set Frame Value = (Block.Color-1)*3 + 2
Now, if you go into the game, the image for the blocks should change when you hover over them, and change back when you move your cursor away.
We have one more animation event to make:
Event: Block>Is Boolean instance variable set Instance variable = IsMatched Action: Block>Set Frame Value = (Block.Color-1)*3 + 3
We also need to make sure that when you hover over a matched block the image doesn't change, so add another condition to the mouse hover event we made:
Condition: Invert: Block>Is Boolean instance variable set Instance variable = IsMatched
Inverting this means that the hover image is only used if the block isn't part of a match. Since we don't have a matching system in place yet you shouldn't see any changes, but as we move forward this event will come into play.
With all three of these events in place, your event sheet should now look like this:
2. Swapping Two Neighboring Blocks
The next thing we will work on is giving the player the ability to swap neighboring blocks. To make this system work we will be using a combination of Arrays and Functions, so if you haven't used them in C2 before, consult the manual to find out more.
What We're Working Towards
Before I explain how to make the system, I want you to see what we are making, and briefly explain how it works. To see what the system will be like, play the demo below. To swap blocks, simply click-and-drag them on top of a neighboring spot in one of the four cardinal directions.
You should notice that when you start dragging a block, four colored squares will appear on top of the blocks that are directly next to it. When you let go of the block there is an event which checks if the block is overlapping any of these colored squares.
If the block is overlapping one of the colored squares, it swaps the block you moved with the block that colored square was drawn on top of or just repositions your moved block to the new location. If your block isn't overlapping one of the colored squares, it does nothing. It also does nothing if the block is overlapping multiple colored squares.
The reason we are using this system, rather than checking whether the block itself is overlapping another block, is that we don't have any easy way of checking where the two blocks are relative to each other. By re-positioning the colored squares into the valid locations and then using them as the basis for our checks, we have a much better idea of what the player intended to do when they tried to make a swap.
Adding the Colored Blocks
Now that we understand what's happening, we can start implementing it. Go to Layout 1 and add a Function object, and an Array object. Leave the Function object's name as
Function, but rename the Array to
BlockPositions. The Function object will allow us to use Functions within our code, and the Array is what we will use to store the positions of the blocks we are swapping.
Once you've done that, create four new sprites to use as our colored squares:
- One green sprite, named
- One red sprite, named
- One yellow sprite, named
- One blue sprite, named
The colors don't actually matter, but using these colors makes each one different so they are easy to identify, and makes it easier to follow along since that's what I did. These sprites should be placed outside of the layout so the player cannot see them.
You should also set the size of each of these to
30,30 and give them each an Opacity of
0. In the demo above, they are each only slightly transparent because I wanted you to be able to see what was happening, but in the actual game the player should not be able to see them at all.
Finally, select your actual Block object and add the Drag & Drop behavior to it.
Now go back to Event Sheet 1 and create a new Event with six Actions.
Event: Block>On DragDrop drag start Action: BlockPositions>Set at XY X=0 Y=0 Value=Block.X Action: BlockPositions>Set at XY X=0 Y=1 Value=Block.Y Action: BottomBlock>Set Position X = Block.X Y = Block.Y -- (Block.Width+2) Action: TopBlock>Set Position X = Block.X Y = Block.Y + (Block.Width+2) Action: LeftBlock>Set Position X = Block.X -- (Block.Width+2) Y = Block.Y Action: RightBlock>Set Position X = Block.X + (Block.Width+2) Y = Block.Y
This event is storing the starting position of the block you moved, and it is moving the colored squares so that they are in the correct positions relative to the block that is being moved.
Your event should look like this:
Figuring Out What the Player Is Doing
The next event we will add will perform the check to see what swap the player is trying to make.
First, the event will listen for when the player 'drops' the block they are moving. It will then perform a series of checks to see which colored square, if any, the block is colliding with, and either perform the appropriate swap, or just reset the moved block's position.
Event: Block>On DragDrop drop Sub-Event: Block>Is overlapping BottomBlock Action: BlockPositions>Set at XY X=1 Y=0 Value = BottomBlock.X Action: BlockPositions>Set at XY X=1 Y=1 Value = BottomBlock.Y Action: Block>Set Position X = BlockPositions.At(0,0) Y = BlockPositions.At(0,1)
This is how it should look so far:
Right now, the Event only accounts for swaps with the Block below the one being moved. To add the others, right-click the sub-event and add an Else. Then, copy the condition from the first sub-event into the Else-event but change the target from
Now, copy the Actions from the original sub-event into the Else-event and again change any instances of
TopBlock. Do this two more times with
RightBlock, so that you have four sub-events total, and so that your event setup looks like this:
Finally, add one more Else-event:
Sub-Event: Else Action: Block>Set Position X = BlockPositions.At(0,0) Y = BlockPositions.At(0,1)
Actually Swapping the Blocks
From here we will implement the swapping mechanic itself. To swap the blocks we will first move the block the player dragged to a location off-screen.
We do this because we have to locate the blocks based on their positions before we can manipulate them - if we started by placing either block in the stored position of the other block it would put them both at the same location and make them very hard to separate from each other. By moving the block to a specific location that we know no other blocks will be in, we prevent either block from ever being in the same location as another block, and prevent the system from having an issue determining which block we want.
We will then move the block they swapped the original block with to the original block's location. Finally, we will move the original block to the location of the second block from its location off-screen, and reset the information in the array to
Event: Function>On function Name: "SwapBlocks" Sub-Event : Condition: Block>Compare X X = BlockPositions.At(0,0) Condition: Block>Compare Y Y = BlockPositions.At(0,1) Action: Block> Set position X = -80 Y = -80 Sub-Event : Condition: Block>Compare X X = BlockPositions.At(1,0) Condition: Block>Compare Y Y = BlockPositions.At(1,1) Action: Block> Set position X = BlockPositions.At(0,0) Y = BlockPositions.At(0,1) Sub-Event: Condition: Block>Compare X X = -80 Condition: Block>Compare Y Y = -80 Action: Block> Set position X = BlockPositions.At(1,0) Y = BlockPositions.At(1,1) Action: BlockPositions>Set at XY X=0 Y=0 Value = 0 Action: BlockPositions>Set at XY X=0 Y=1 Value = 0 Action: BlockPositions>Set at XY X=1 Y=0 Value = 0 Action: BlockPositions>Set at XY X=1 Y=1 Value = 0
Your Event sheet for that should look like this:
Now, go back to the four checks we made earlier that determined which colored square the dragged block was colliding with, and add this Action to the end of the Actions list for all four:
Action: Function>Call function Name: "SwapBlocks"
Making Sure the Player Meant to Do That
If you run the game now, it will work almost perfectly. The only thing it doesn't do yet is check to see if the block is overlapping multiple colored squares. (If it is, we don't want anything to happen because it means the move may be a mistake.)
Currently it checks the colored squares in the order
Right, and whichever it finds first, it swaps with. To fix this we have to add two inverted conditions to each check.
To the Top and Bottom checks, add these conditions:
Condition: Inverted: Block>Is overlapping another object Object: LeftBlock Condition: Inverted: Block>Is overlapping another object Object: RightBlock
To the Right and Left checks, add these conditions:
Condition: Inverted: Block>Is overlapping another object Object: TopBlock Condition: Inverted: Block>Is overlapping another object Object: BottomBlock
Since the squares are only 30x30px, it's impossible for the block to be overlapping two on opposite ends at the same time. These checks allow us to make sure the dragging movement isn't skewed to either side too much, and ensures the player has a mostly straight motion so it is clear we are detecting the right kind of swap.
Your Event sheet should now look like this:
And here's a demo of our game in its current state:
We now have a fully functional swapping mechanic. In the next tutorial we'll set up a strong match detection system and start to really see the gameplay in action.
If you want to continue working on your own, you should look at how you could detect when two blocks are the same color and how you would know whether they are next to each other and whether they form a full group. A good place to start would be with a
System > For Each event with the
Block object, and perform some kind of check on each
I hope you enjoyed this tutorial and are enjoying the series so far; make sure you come back soon for the next installment.