How do you make platformers using arrays as sensing?
Like using this:
1111111111
1000000001
1000000001
1000000001
1000000021
1111111111
as your level, and using letter () of () for sensing?
EDIT: 1600'th topic in MaC
Last edited by TorbyFork234 (2012-08-27 19:59:15)
Offline
Check out Music Marathon; the platforming is entirely array-based. If you want me to explain it step by step, I can do that as well, but please look at the scripts first.
Last edited by amcerbu (2012-08-27 20:09:49)
Offline
I'm assuming that
squareX = round((xPos - 12) / 24) * 24 + 12
squareY = round(yPos / 24) * 24
Array # = (15 * ((squareX - 12) / 24 + 10)) + (8 - (squareY / 24))
is the main thing for sensing. Where you would then get the item (array #) of (level data), and you would then base the interaction based on what number you get.
From what I'm getting,
squareX= round((xPos-(tile width/2))/(tile width))*(tile width)+(tile width /2)
square y=round (ypos / (tile width))*(tile width)
------------------------
I don't really understand how that is supposed to give the answer, so can you explain it? Not step by step, but how you made that equation.In the project the "Physics" Sprite just plopped down that equation, no explanation of how that got your answer.
Thanks!
Offline
You've analyzed it correctly. It's a variation on a classic grid-snapping equation, which involves dividing, rounding, and multiplying. The array number is based on the principle that the array looks like this:
Numbers represent the location of each tile type in the LevelData list. For example, "127" means that item 127 of list LevelData would occupy the spot at the 9th column and 7th row. 001 016 031 046 061 076 091 106 121 136 151 166 181 196 211 226 241 256 271 286 002 017 032 047 062 077 092 107 122 137 152 167 182 197 212 227 242 257 272 287 003 018 033 048 063 078 093 108 123 138 153 168 183 198 213 228 243 258 273 288 004 019 034 049 064 079 094 109 124 139 154 169 184 199 214 229 244 259 274 289 005 020 035 050 065 080 095 110 125 140 155 170 185 200 215 230 245 260 275 290 006 021 036 051 066 081 096 111 126 141 156 171 186 201 216 231 246 261 276 291 007 022 037 052 067 082 097 112 127 142 157 172 187 202 217 232 247 262 277 292 008 023 038 053 068 083 098 113 128 143 158 173 188 203 218 233 248 263 278 293 009 024 039 054 069 084 099 114 129 144 159 174 189 204 219 234 249 264 279 294 010 025 040 055 070 085 100 115 130 145 160 175 190 205 220 235 250 265 280 295 011 026 042 056 071 086 101 116 131 146 161 176 191 206 221 236 251 266 281 296 012 027 042 057 072 087 102 117 132 147 162 177 192 207 222 237 252 267 282 297 013 028 043 058 073 088 103 118 133 148 163 178 193 208 223 238 253 268 283 298 014 029 044 059 074 089 104 119 134 149 164 179 194 209 224 239 254 269 284 299 015 030 045 060 075 090 105 120 135 150 165 180 195 210 225 240 255 270 285 300
Offline
But the important physics stuff doesn't have to do with finding out where the player is (as this is very much dependent on your own game's tile setup). The unique physics part comes into play with how the program tells the player to react with tiles.
I've written an (incomplete) documentation, which I can post if you'd like to read through. It explains some of this stuff a bit better.
Offline
Music Marathon Documentation and Description
This post contains the documentation and in-depth description of the scripts used in Music Marathon, a collaborative effort on the part of Hardmath123, applejack, and myself, amcerbu. This project is still in development as of 4/15/12.
Overview
Music Marathon is a music-related platforming game, in which the player directs the character, “Penguinote,” (whimsically named by applejack) to collect powerups, musical notes, and reach the end of the level in a limited amount of time. This post documents the physics scripts used in the project. The method employed could be called “array-based platforming,” as only the data from a list is used to calculate physics responses. To improve reliability and accuracy, no “touching color” or “touching sprite” blocks were used in the entire project.
Program/Logic Structure
The main execution of the program happens from the "game loop," a single script in the character sprite that controls the rest of the program. It broadcasts messages that ultimately add up to updating and drawing. Since the messages are executed in sequence (using “broadcast and wait” blocks) the control is equivalent to a 1s1, where there is a definitive division between frames – there aren’t multiple forever loops executing “simultaneously.” This improves the fluency and reliability of the game.
Level Storage
In order to facilitate level design and multi-level gameplay, each level is stored as a list of 300 numbers. The level itself is composed of a grid of 20 x 15 tiles, each 24 x 24 pixels. The layout of the level would look like this, where numbers represent location in the level list:
Numbers represent the location of each tile type in the LevelData list. For example, "127" means that item 127 of list LevelData would occupy the spot at the 9th column and 7th row. 001 016 031 046 061 076 091 106 121 136 151 166 181 196 211 226 241 256 271 286 002 017 032 047 062 077 092 107 122 137 152 167 182 197 212 227 242 257 272 287 003 018 033 048 063 078 093 108 123 138 153 168 183 198 213 228 243 258 273 288 004 019 034 049 064 079 094 109 124 139 154 169 184 199 214 229 244 259 274 289 005 020 035 050 065 080 095 110 125 140 155 170 185 200 215 230 245 260 275 290 006 021 036 051 066 081 096 111 126 141 156 171 186 201 216 231 246 261 276 291 007 022 037 052 067 082 097 112 127 142 157 172 187 202 217 232 247 262 277 292 008 023 038 053 068 083 098 113 128 143 158 173 188 203 218 233 248 263 278 293 009 024 039 054 069 084 099 114 129 144 159 174 189 204 219 234 249 264 279 294 010 025 040 055 070 085 100 115 130 145 160 175 190 205 220 235 250 265 280 295 011 026 042 056 071 086 101 116 131 146 161 176 191 206 221 236 251 266 281 296 012 027 042 057 072 087 102 117 132 147 162 177 192 207 222 237 252 267 282 297 013 028 043 058 073 088 103 118 133 148 163 178 193 208 223 238 253 268 283 298 014 029 044 059 074 089 104 119 134 149 164 179 194 209 224 239 254 269 284 299 015 030 045 060 075 090 105 120 135 150 165 180 195 210 225 240 255 270 285 300
The actual elements of the list are all numbers between 1 and 6, where each number represents the type of tile printed at the location indicated above.
Type 1- 45 degree slope, bottom left to top right, filled below
Type 2- 45 degree slope, bottom right to top left, filled below
Type 3- 45 degree slope, top right to bottom left, filled above
Type 4- 45 degree slope, top left to bottom right, filled above
Type 5- Filled square
Type 6- Empty square
Fortunately, since there are fewer than 10 tiletypes, the entire LevelData list can be “compressed” into a string of 300 digits, and stored in another list. This allows the game to store multiple levels (theoretically, an unlimited number). When a level is needed, it is copied out into the LevelData list.
The Player
The "active zone" of the player – the part that interacts with the level – is 17x17 pixels. The actual player sprite is larger, to support decoration.
The player has many variables in his palette (download the project and see for yourself), which store data relating to particular interactions with different tiles. Most of these are self-explanatory, and we need only to be concerned with:
Position.X and Position.Y - These are important to the smoothness of the game. Rather than telling the player to
set x to [some number] set y to [some number]... we can ...
set [Position.X v] to [some number] set [Position.Y v] to [some number]... and then later on in the script ...
go to x: (Position.X) y: (Position.Y)This gives us more control over where the player actually is, so corrections for positions (physics) are performed on Position.X, before the sprite ever actually occupies that position on the screen.
x represents the location of the player o o o o x o o o o
The player could be touching surfaces on any of the nine tiles indicated.
We retrieve the tile-types of the nearest 9 squares, as indicated above, and store them to variables. For the purposes of this post, we will call the variables "1TileType,” "2TileType," ... "9TileType." The numbers in the variables’ names indicate only the location of the tile relative to the player, not the type itself. Depending on a tile’s location, the player will interact differently with it. For example, the angled tiles include slopes, flat "ground" tiles, and corners. If the player were to stand above an angled tile, he will react differently than if standing beneath it.
These are the labels we apply to the surrounding tiles. We store the tile type of location 1 in the variable 1TileType, etc. 1 2 3 4 5 6 7 8 9
We create certain conditions for the specific interactions with different tiles. Here is an example, for the 1 tile.
Tile type 1 looks a bit like this (though at 45 degrees, which ASCII art cannot quite express):
/| / | /__|
If we create a 3-by-3 square of these tiles, and consider the player to be in the middle, we can see where and how he would interact with each different tile location.
/| /| /| /1| /2| /3| /__| /__| /__| /| /| /| /4| /5| /6| /__| /__| /__| /| /| /| /7| /8| /9| /__| /__| /__|
I have labeled each with the tile location the program would see.
We must look at each labeled tile individually, not as a group of 9. Imagine a scenario where the player is on location 5 (with no tile there), and there is a tile only at location 1. We can then determine the character's appropriate response. This is repeated for each tile, and we come up with the following list:
Location 1: corner
Location 2: ceiling
Location 3: corner
Location 4: left wall
Location 5: slope
Location 6: slope
Location 7: corner
Location 8: slope
Location 9: nothing. If the player is at the central square, he never will touch label 9's tile.
These interaction types are stored in a list. We create 4 more lists for storing the interaction types for each of the 5 relevant tile types (we don't need one for tile type 6 - it is the empty tile, and is used as a placeholder for the sake of level editing).
Vectors
Before we start talking about specific physics scripts, we have to be familiar with the concept of the vector. For more information about vectors, check out Wikipedia- Vector and Wikipedia- Vector Projection. Vector-related physics scripts are used in Slopes and for the corners and sloped surfaces in this project.
A vector is best expressed by the idea of a "moving point." Vector <2,2> (that's the notation you use) means moving some point from its original location to a new location, 2 units right, and 2 up. You can think of a vector as a line segment with a direction. Velocity, for example, is a vector, where the values of x- and y-velocity change x- and y-position. Likewise, the surface of a collision can be represented as a vector.
For this project, we are particularly interested in what's called "Vector Projection." The projection of vector a onto vector b yields a new vector, c, which points in the direction of b, but has a length corresponding to a's length along b. The Wikipedia article linked above has a pretty good diagram representing this. There are two steps in the calculation we use for projecting vectors: The first is to determine what's called the scalar projection (a scalar is a number, not a vector), which is then multiplied by one of the vectors.
Scalar Projection (a onto b) =
((a.x * b.x) + (a.y * b.y)) / magn(b)^2
Where a.x is a's x-component and magn(b) is the magnitude, or length, of vector b. Note that magnitude is often represented with vertical bars (||).
Then, our new vector, c, is equal to:
c = <(Scalar Projection)(b.x), (Scalar Projection)(b.y)>
(This is just the multiplication of a vector by a "scale.")
This new vector, c, represents the length and direction of a along b.
Physics Scripts
There are three main types of physics interactions:
1. Wall
Wall interactions (and ceiling/floor interactions) are the simplest of the three. Traditionally, if a player hits a vertical (left- or right-facing) wall, his x-velocity is set to 0 (to stop), or negated (to bounce). Similarly, his x-position is adjusted so that he is touching, but not intersecting, the wall. Likewise, vertical (ceiling and floor) walls deduct from y-velocity and alter y-position. Here is an example of the floor interaction script:
when I receive [WallDown v]
if <(Position.Y) < ((Position.Y) - (_W-D-Distance))> // _W-D-Distance is custom-tuned by the programmer.
set [Acceleration.CanJump? v] to [1] // Used later in the script to allow jumps.
set [Position.Y v] to ((Position.CurrentTileY) - (_W-D-Distance)) // Sets y-position to the floor.
set [Velocity.Y v] to [0]
if <not < <[KeyPressed v] contains [Right]> or <[KeyPressed v] contains [Left]> > > // List for currently-pressed keys.
set [Velocity.X v] to ((Velocity.X) * (Acceleration.Friction)) // Slow down by coefficient of friction.
There are four of these "when I receive" hats for the four different types of walls. Only the floor script contains a "set Acceleration.CanJump? to 1", since the player can only jump off the floor. If I were to add wall-jumping, it would set that variable to some other value when the player was touching a right- or left-facing wall. when I receive [CornerDownLeft v] set [Surroundings.DistanceToCorner v] to ([sqrt v] of ( (((Position.X) - ((Position.CurrentTileX) - [12])) * ((Position.X) - ((Position.CurrentTileX) - [12]))) + (((Position.Y) - ((Position.CurrentTileY) - [12])) * ((Position.Y) - ((Position.CurrentTileY) - [12])))) ) if <(Surroundings.DistanceToCorner) < (_C-D-L-Distance)> // _C-D-L-Distance is a programmer-specified value. set [Position.X v] to (((((Position.X) - ((Position.CurrentTileX) - [12])) * (_C-D-L-Distance)) / (Surroundings.DistanceToCorner)) + ((Position.CurrentTileX) - [12])) set [Position.Y v] to (((((Position.Y) - ((Position.CurrentTileY) - [12])) * (_C-D-L-Distance)) / (Surroundings.DistanceToCorner)) + ((Position.CurrentTileY) - [12])) set [Surface.X v] to ((Position.Y) - ((Position.CurrentTileY) - [12])) // The perpendicular to vector <x, y> is set [Surface.Y v] to ([-1] * ((Position.X) - ((Position.CurrentTileX) - [12]))) // vector <y, -x>. set [Surface.MagnitudeSquared v] to (((Surface.X) * (Surface.X)) + ((Surface.Y) * (Surface.Y))) set [Surface.Scalar v] to ((((Velocity.X) * (Surface.X)) + ((Velocity.Y) * (Surface.Y))) / (Surface.MagnitudeSquared)) set [Velocity.X v] to ((Surface.X) * (Surface.Scalar)) set [Velocity.Y v] to ((Surface.Y) * (Surface.Scalar))
Offline
Ok. But that really didn't help me. Can you explain to me that red parts in this:
Array # = (15 * ((squareX - 12) / 24 + 10)) + (8 - (squareY / 24))
the squareX-12 is red because before you added 12 at the very end of squareX, why would you add 12, just to have it subtracted immediately? (the other ones I have no clue why they're there.)
EDIT: Outpost, you posted that long block of text quick, I'll read it.
Last edited by TorbyFork234 (2012-08-27 20:46:19)
Offline
I just read through it, and the part that I need is the one where you put in parentheses:
(if you're unfamiliar with this type of grid-snapping, look around on the Scratch website) under physics interaction.
Offline
Yeah, I'm looking at that right now and trying to figure out why on earth I did that...
To be honest, it's been so long that I can't remember what that script is for. I really cannot understand my own logic. I would recommend that your array go from bottom left-hand corner to top right-hand corner, across first. That would align more naturally with the built-in coordinate system of Scratch.
Last edited by amcerbu (2012-08-27 21:37:51)
Offline
amcerbu wrote:
Yeah, I'm looking at that right now and trying to figure out why on earth I did that...
maybe its to align the squares to the proper grid or to offset badly placed costume centers
Offline
Yeah... perhaps you should build a level editor first and then go from there. That's how I did it. I'll have to look at how you're setting up your game to know exactly what the grid-snapping technique should be. For example, how large are your tiles? How many tiles are there in each row and column? Do you intend to use only square tiles, or also slanted, or sloped tiles?
Offline
Hey, amcerbu, on my thread, the one with scrolling projectiles, I'd like to personally request your help since it's a math issue, and your very good at math. I need an answer to it ASAP, since the project is due very soon. Thanks!
Last edited by sonicfan12p (2012-08-30 01:19:35)
Offline