Computer Science Canada

[Tutorial] 2-D mapping I (eg. Tetris)

Author:  mwachna [ Wed Mar 09, 2005 8:42 pm ]
Post subject:  [Tutorial] 2-D mapping I (eg. Tetris)

2-D Mapping

2-D mapping can be useful in creating games similar to Zelda, and Tetris. This tutorial will cover the following topics:

1.1-What is an array?
1.2.-What is a 2-D ray?
1.3-What is an array of an array?
2.0-Tetris games
2.1-Creating the screen
2.2-Creating the pieces
2.3-Moving through empty space
2.4-Hitting block bottom
2.5-Game over

1.0 Introduction:
This is a brief overview of mapping in 2-D. This will just be a general eye-opener, or a push forward in 2-D mapping.

1.1 An array is a grouping a variables with the same name, just different parameters (a parameter is similar to an independent variable in the way that you may manipulate it to create a different outcome). Such as in math: f(x) is an array of points. 'f' is a function, and 'x' is a variable. For every value of 'x', f(x) will store a dependent value. So if f(x) represents a function of addition for 3x-x then the first 3 values of 'f' is:
f(1)=2
f(2)=4
f(3)=6

In Turing, however, we can assign these values. So if, we were to want to store the finishing times of runners in a race, we can use an array with the limits equaling the number of runners in the race.
In this example, we will input the results for 100 racers, using their tag number for identification. We will store the results by MM.SS
code:

var tag: int
var runner: array 1..100 of real
for rep 1..100 of int
        get tag
        get runner(tag)
put "Place ", rep, " - Runner ", tag, " : ", runner(tag)
end for


This code just stored the results of the race in an array while at the same time out putting them as:

Place 1 - Runner 32 : 23.33
Place 2 - Runner 1 : 24.21
Place 2 - Runner 93 : 24.55
"¦etc

1.2 2-D arrays are just arrays with more then one parameter, or dependent variable. So if we wanted to group a population by their age and sex then we would need a 2-D variable as such:

code:

var age, sex: int
var population: array 1..100, 1..2 of int
loop
        get age
        get sex
        if age>100 then
                age:=100
        elsif age <1 then
                age:=1
        end if
        population(age, sex)+=1
end loop


This code will tally the population as entered in by the user as per age and sex, with all ages 100+ and ages 1- falling into their own categories.

1.3 An array of an array is basically a multi-dimensional array that is broken up into sub groupings. The difference is mainly in the organization of the array. Briefly, in this example, we will input the class room number according to the course code, course level (in the olden days enriched, advanced, basic, and general"¦nowadays its something like university, college, etc"¦.right now we are going to stick with the olden days)"¦and then individualize these classes by class section (assuming there are 3 sections of each).
The course code for this example is being organized through an SYCC code (subject, year, specific course number).

code:

var classRoom: array 1..9000, 1..4 of array 1..3 of int
for Subject 1..9
        for Year: 1..4
                for Course: 1..20
                        for Level 1..4
                                for Section 1..3
                                        get classRoom (Subject*1000+Year*100+Course,
Level) (Section)
                                end for
                        end for
                end for
        end for
end for


This code assumes that there are only 9 possible subjects, 4 years of study, and 20 different courses per year of study in each subject. This code's multiple for loops just cuts back on time and doesn't create classes that do not exist (ie. 3954).
If a student wanted to access this data base to find his/ her room, we can use this code:

code:

get Class
get lv
get sec
put classroom (Class, lv) (sec)


A sample input for this would be 5212 + 2 + 1 (subject #5, 2nd year, course 12) (level 2), (section 1).

2.0 Go play some Tetris"¦the rest of this tutorial will fly by.

2.1 Determine the size and location of the playing screen. For this example, we will locate the bottom left corner at (100, 0) with dimensions of 100x200.
Next determine the size of the blocks (note each piece is broken up into blocks), for this example, we will use 10x10.

Next we need to set up our array. Since our screen is divided into 10x10 blocks, we need an array of 10x20. We need to then set the screen as empty space (colour 0).

code:

var Space: array 1..10, 1..20 or int
for x 1..10
        for y 1..20
                Space (x,y):=0
        end for
end for


Now we simply draw the map (which can be done as we set the Space values).
code:

drawfillbox(90+10*x, y*10-10, 100+10*x, y*10, Space (x,y))


2.2 Now we need to create arrays for each of the blocks that we have. This is a good time to use an array of an array as we want to create an area similar for each block that will incompass the entire figure.
We will have 3 blocks (written in text code):
00000 00000 00000
00000 00200 03000
01110 02220 03000
00000 00200 03330
00000 00000 00000
The three blocks are in a 5x5 box as you can see. Each 0 represents black, empty space, and each other number represents a different colour unique to each type of block.
Now to make life easier, lets put this into a text file so we can input the values. Set up your text file with each of the blocks inverted (note: look at the pattern of the third block and how it is inverted in the text so that it shows up as an "L" in the game). Make sure to include spaces between values.
0 0 0 0 0
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 2 0 0
0 2 2 2 0
0 0 2 0 0
0 0 0 0 0
0 0 0 0 0
0 3 3 3 0
0 3 0 0 0
0 3 0 0 0
0 0 0 0 0
After naming this text file, open the file up in you program and input the co-ordinates of each array's array as so:
code:

var Piece: array 1..3 of array 1..5, 1..5 of int
for Type: 1..3
        for y: 1..5
                for x:1..5
                        get: fileno, Piece (Type) (x,y)
                end for
        end for
end for

2.3 When moving downwards, or side ways, we need to check to see if there is empty space to move into. We need to create a temporary variable, 'tempiece' that will be an array'd image of the current piece.

You need to assign the variable the same values as the piece which was randomly selected. After this, we need to check to see if there is sufficient empty space to move through, using 'if' statements.
code:

var tempiece: array 1..5, 1..5 of int
var next: int
randomize (next, 1, 3)
for y:1..5
for x:1..5
                tempiece (x,y):=Piece (next) (x,y)
        end for
end for

Now we need to check to see if you can move down still
code:

var locX, locY: int
locX:=5  %always make sure these variables are
locY:=20                %reset to the same starting values for each new piece.
loop
for x: 0..4
for decreasing y: 5..1
                        if Space (locX+x, locY-y) not=0 then
                                if tempiece (1+x, y) not=0 then
%this is where you tell the program that the %block  can no longer move down. (exit out %of loop)
                                        end if
                                end if
                        end for
                       end for
                       locX+=1
                       locY+=1
                       %redraw the piece here (see below)
                end loop
               

When you redraw the piece use 2 for loops and drawfillbox as such:
code:

for x:1..5
        for decreasing y: 1 .. 5
                if tempiece(x,y) not=0 then
                        drawfillbox (100+(locX+x-2)*10,(locY+y-2)*10,100+(locX+x-
1)*10, (locY*y-1), next)
                        if tempiece(x,y+1) =0 then
drawfillbox (100+(locX+x-2)*10,(locY+y-
1)*10,100+(locX+x-1)*10, (locY*y), 0)
                        end if
                end if
        end for
end for


2.4 When the piece hit the bottom of the screen, you need to check for solid lines, and then you need to re-adjust the map's array.

To do this, simply determine the highest point of the 'tempiece', and start an array leading up to its location for the 'y' value, and up to 10 for the 'x' value.

Create a boolean statement that turns false each time a black spot exists in the row. Other wise, if at the end of the row the value is true, then mark a score, store the variable for row number that was solid, and clear out that row.

After going through the entire block bottom once, go through it again, and move in on empty rows.

2.5 When the top limits of the 'tempiece' stops at the top of the map (y=20) then the game is over"¦its that simple.

I will expand on 2-D arrays to include larger maps that do not fit on one screen. The proceeding sections will follow as such:

3.0-Zelda type RPG games
3.1-Larger then screen-fit maps
3.2-Loading/creating maps
3.3-Drawing graphics
3.4-Moving foes on different textures
3.5-Properties of clipping
3.6-Map scrolling

This tutorial is not a complete how to...its a "get you started"...you don't learn by pasting and copying!
This tutorial was inspired by azndragon

-matt
-Go T-Cats!-

Author:  mwachna [ Wed Mar 09, 2005 9:14 pm ]
Post subject: 

sorry guys, some of that code is just really out of line!

Author:  ssr [ Wed Mar 09, 2005 9:21 pm ]
Post subject: 

nice turtoial
Laughing 8)

Author:  The_$hit [ Sat Mar 19, 2005 7:15 pm ]
Post subject: 

How would you make sure that you dont run into any of the things on the map?

Author:  Cervantes [ Sat Mar 19, 2005 8:40 pm ]
Post subject: 

mwacha wrote:
you don't learn by pasting and copying!

Lol, you don't get anywhere (you want) by pasting and copying. It's copy then paste. Laughing
But seriously: this is a nice, well-planned tutorial. Looking forward to seeing the next section!
The_$hit wrote:
How would you make sure that you dont run into any of the things on the map?

Collision detection. To determine if the peice is overlapping with a piece on the map:
code:

var Piece : array 1 .. 4 of %each tetris piece is made of 4 blocks.
  record
    x, y : int
  end record
for i : 1 .. upper (Piece)
if Space (Piece (i).x, Piece (i).y) not= 0 then %if the location of the segment of the piece you are checking is not in empty space
  %COLLISION!!  GAME OVER!!  err...actually, just make a new piece and stop moving that piece down, and add it's data points into the map.
end if

Mind you, we don't want to determine whether the piece overlaps a piece on the map. We want to determine if the piece collides. So, subtract one from the y value of the segment of the piece.
code:

if Space (Piece (i).x, Piece (i).y - 1) not= 0 then

Author:  The_$hit [ Sat Mar 19, 2005 11:25 pm ]
Post subject: 

Thank you for that explanation. I am wondering if you know how to detect for collision in an rpg style game? I am having trouble because there would be so many objects to check for, and I have to include it in the main loop using as little code as possible.

Author:  Cervantes [ Sun Mar 20, 2005 8:35 am ]
Post subject: 

That would depend on the style of the RPG. If it's something like Zelda, that uses a grid, it shouldn't be too difficult or take too much processing power. However, if it's something that doesn't use a grid, well, it'll be harder. If that's the case, ask in the Turing Help forum. We don't want to flood mwachna's thread Wink If it will be like Zelda, let's wait and see what mwachna says! Smile

Author:  dann_west [ Mon May 16, 2005 6:59 pm ]
Post subject: 

wow. That really put me ta sleep. You should get more ussful info!
Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil

Author:  lain [ Mon May 16, 2005 7:16 pm ]
Post subject: 

dann_west wrote:
wow. That really put me ta sleep. You should get more ussful info!
Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil


Actucaly this info is usefull, it is the fundmentals of how alot of the 1st graphical rpgs where made and alot of the snes games where made. Since truing has limited power it is also the best way to make games of this style in my option.

Author:  CodeMonkey2000 [ Sat Nov 18, 2006 12:47 pm ]
Post subject: 

wow, this really is useful. i think mwuaha is inactive, can someone else like update this? this whole concept is still new to me. the code isnt well comented, so how do i draw a peice and move it around?
heres my code right now:

code:
setscreen ("graphics:100;200")
var Space : array 0 .. 9, 0 .. 19 of int
var p : int := 0
for x : 0 .. 9
    for y : 0 .. 19
        Space (x, y) := 0
        drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, Space (x, y))
        p += 1
    end for
end for
put p
var Piece : array 1 .. 3 of array 0 .. 10, 0 .. 20 of int

var fileName : string := "map.t"
var fileNo : int := 0

open : fileNo, fileName, get
for decreasing y : 19 .. 0
    for x : 0 .. 9
        get : fileNo, Space (x, y)
    end for
end for
close : fileNo

for y : 0 .. 19
    for x : 0 .. 9
        if Space (x, y) = 0 then
            drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, black)
        elsif Space (x, y) = 1 then
            drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, green)
        elsif Space (x, y) = 2 then
            drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, red)
        elsif Space (x, y) = 3 then
            drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, blue)
        end if
    end for
end for


and heres the map

code:

0 0 0 0 0 0 0 0 0 0
0 2 2 2 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 3 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 0 0 0 0 3 0 0 0
0 3 3 3 3 3 3 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

Author:  DemonZ [ Sat Nov 18, 2006 2:49 pm ]
Post subject: 

question about moving through the 2d map, how do you have the player move through the map onto each box, like so the player doesnt go off the box, its hard to explain, how do you have the player move from box to box, like so the player doesnt just push the control and he only goes 1 space instead of going into the box space. hope u guys know what I mean.

Author:  ericfourfour [ Sat Nov 18, 2006 3:01 pm ]
Post subject: 

@spearmonkey2000
First things first.

code:
var fileName : string := "map.t"

Give the file a different extension than "t". It is not going to kill your program if you leave it that way but you should change it because that extension is for Turing source code.

Next, you want to check for input. Look up Input.KeyDown for this.

For your piece give it coordinates and get input to figure which of the coordinates to change. For example if the user presses the up button increase the y coordinate and If the user presses the left button decrease the x coordinate...etc.

For displaying the character start with a box or something simple and simple draw it at the player's coordinate.

About updating this tutorial. Maybe, I should write a tutorial like this except it will be object oriented, use 1D arrays instead of 2D arrays, and have layers. Maybe, I could throw a shortest path algorithm in there as well.

@Demonz
I have no idea what you are asking but I think you are looking for collision detection. All you have to do is check if the next tile is occupied.

Author:  DemonZ [ Sat Nov 18, 2006 6:25 pm ]
Post subject: 

no no no! im not talking about controls or collision, example: when u play tetris, and move piece, by how much are u moving it by? thats what I meant, or in Pacman, when u move him, he moves lets say 30 spaces then stops, how do u do that?

Author:  DemonZ [ Sat Nov 18, 2006 6:29 pm ]
Post subject: 

ok I think I can explain it better now. Lets say u have a grid as ur map that is 100 x 100, and u want the size of the player to occupy 1 grid, and u want to move the player from 1 grid to another, so lets say u press left, you will go into the left grid box, or if u go right, u go into the right grid box, and u do not want the player moving out of those lines of the grid, he must be inside the box, and he can only go vertical or horizontal, not diagonal, how do you do that, so that when u move the player, u move him according to grids, not on his own space or time.

Author:  ericfourfour [ Sat Nov 18, 2006 6:49 pm ]
Post subject: 

I guess the spaces about which you are talking are pixels, right? The other thing you are talking about is tiles, right?

Do you mean you want an animation while the entity is moving from one tile to the next or to just have the entity move without an animation?

If you want an animation you need to have a counter variable. Every loop it would increase. Let's say the tiles were 32 pixels wide. When the counter reaches 32 the entity would be at the next tile. And during the animation it would be occupying the tile from which it is leaving and the tile to which it is moving. While the entity is moving simply have a flag that disables movement input until the animation is complete.

If you just want to move the entity to the next tile directly, add the width or height of the tile depending on the direction.

Author:  DemonZ [ Sat Nov 18, 2006 7:42 pm ]
Post subject: 

yes thats it thanks i want it to go from tile to tile. can u show me how u do that?

Author:  ericfourfour [ Sat Nov 18, 2006 7:57 pm ]
Post subject: 

It is not that hard to do. I basically outlined it in my previous post. It should go like this though.

code:
loop
    for every entity
        if entity is not in motion
            allow entity to choose direction (make sure they can walk there too)
            if the entity will move
                activate animation for that entity corresponding to the direction
                block of tiles the entity is moving to and from
                disable movement for this entity
end loop

I think that is everything. I'm in a rush!

Author:  CodeMonkey2000 [ Sat Nov 18, 2006 10:27 pm ]
Post subject: 

Confused now i have a problem with collision.
i ont think i did this right
heres my code:

code:

setscreen ("graphics:100;200,offscreenonly")
var Space : array 0 .. 9, 0 .. 19 of int
var tilex : flexible array 0 .. 0 of int
var tiley : flexible array 0 .. 0 of int
var key : array char of boolean
var player :
    record
        x, y : int
    end record

player.x := 0
player.y := 0

var p : int := 0
for x : 0 .. 9
    for y : 0 .. 19
        Space (x, y) := 0
        drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, Space (x, y))
        p += 1
    end for
end for

var Piece : array 1 .. 3 of array 0 .. 10, 0 .. 20 of int

var fileName : string := "map.t"
var fileNo : int := 0
var e : int
open : fileNo, fileName, get
for decreasing y : 19 .. 0
    for x : 0 .. 9
        get : fileNo, Space (x, y)

        if Space (x, y) > 0 then
            for t : 0 .. 10
                new tilex, upper (tilex) + 1
                e := upper (tilex)
                tilex (e) := 10 * x + t

                new tiley, upper (tiley) + 1
                e := upper (tiley)
                tiley (e) := 10 * y + t
            end for
        end if

    end for
end for
close : fileNo

proc redraw
    for y : 0 .. 19
        for x : 0 .. 9
            if Space (x, y) = 0 then
                drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, black)
            elsif Space (x, y) = 1 then
                drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, green)
            elsif Space (x, y) = 2 then
                drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, red)
            elsif Space (x, y) = 3 then
                drawfillbox (10 * x, y * 10, 10 + 10 * x, y * 10 + 10, blue)
            end if
        end for
    end for
end redraw

fcn collide (x, y : int) : boolean
    var go : boolean := true

    for tx : 1 .. upper (tilex)
        if x = tilex (tx) then
            for ty : 1 .. upper (tiley)
                if y = tilex (ty) then
                    go := false
                end if
            end for
        end if
    end for

    result go
end collide
loop
    Input.KeyDown (key)

    redraw
    if key ('w') and collide (player.x, player.y + 5) then
        player.y += 5
    end if
    if key ('s') and collide (player.x, player.y - 5) then
        player.y -= 5
    end if
    if key ('d') and collide (player.x + 5, player.y) then
        player.x += 5
    end if
    if key ('a') and collide (player.x - 5, player.y) then
        player.x -= 5
    end if
    drawfillbox (player.x, player.y, player.x + 10, player.y + 10, yellow)
    View.Update
    Time.DelaySinceLast (50)
end loop


Author:  DemonZ [ Sat Nov 18, 2006 10:52 pm ]
Post subject: 

ok thanks alot im gonna test this theory now (not really a theory more like a solution!)

Author:  ZeroPaladn [ Mon Nov 20, 2006 1:31 pm ]
Post subject: 

Nice tutorial! Didn't know you could make an array of an array Laughing . It was a litle hard to read though.

Author:  ericfourfour [ Mon Nov 20, 2006 3:31 pm ]
Post subject: 

When you first start 2D mapping I would recommend using 2D arrays. Once you are more experienced a 1D array will be easier to work with (have you ever tried working with a 2D flexible array?). There are however a few things you should know before working with a 1D array.

(This is with a 1D array with the index starting at 0)

The upper bound of the array.
code:
height * width - 1


Converting coordinates to an index.
code:
x + y * width


Converting an index to coordinates.
code:
x: index mod width
y: (index - xFormula) / width

Author:  CodeMonkey2000 [ Tue Nov 21, 2006 6:51 pm ]
Post subject: 

theres no such thing as a 2d flexible array yet in turing. it says "feature not available. unable to allocate memory-sorry for any inconvieance"

Author:  ericfourfour [ Tue Nov 21, 2006 8:22 pm ]
Post subject: 

That is exactly my point. The 2D flexible array does work however. You can set the size of the second dimension once but after that you cannot change to a different size. The first dimension can be changed as often as you like. This can get tedious. That is why a 1D array is easier.

Author:  CodeMonkey2000 [ Tue Nov 21, 2006 10:20 pm ]
Post subject: 

aww ok i get it. array 1..5,1..5 has 25 elements in it anyway so just declare 1..25. 2d arrays are just ways of organizing data

Author:  ericfourfour [ Tue Nov 21, 2006 11:03 pm ]
Post subject: 

Yes, that is it exactly. There is one other thing though. You should be careful if the arrays are going to start at 1. You will have to add 1 to the formulae I previously mentioned to get the proper result.


: