The original text is too long, so the translation and the original text are divided into two parts, mainly about the tile rules of 2D games and the principle of irregular rendering and role collision.
Because the development of simple 2D games is too simple, this article attempts to combine different 2D flat games and classify them to list their advantages and disadvantages, and then discuss some implementation details. The long-term goal is to provide comprehensive guidance on 2D flat game development. If you have any suggestions, corrections, requirements, or supplements, please leave a message!
Declaration: some articles use The Decompilation engine instead of code or programmers. They are very willing to implement it in this way, but it just seems like this! In addition, the size of the game tile is set for the game logic, which is different from the actual size!
Four Solutions
From Simplicity to complexity, I will first list the four common 2D game implementation solutions I know:
Solution 1: Pure tile Rendering
In this mode, the movement range of the game role is completely restricted by the map tile. Under normal circumstances, the role cannot stand in the center of two adjacent tile.
What we see with the naked eye is progressive role Movement (animation), which is just an exercise to make the game look smoother. What it eventually reaches is only a certain tile range, it is impossible to locate the two tiles at the same time (for example, the role can be moved freely between the coordinates (0, 1) and (0, 0, however, each actual location determination can only take one of the two coordinates, and the location cannot be (0, 0.5 ).
This solution is the easiest to implement, but exposes many serious restrictions on role control, especially in traditional action games. However, this solution was unexpectedly welcomed in pixel-style games or cartoon games.
Example: Prince Persian, Toki Tori, Lode Runner, Flashback
How to implement it?
Because the map is grid-like, every tile area (usually in the form of a two-dimensional array) stores information about whether it is an obstacle and whether it can pass.
Of course, sometimes it can contain additional pictures, information about the role's footsteps and sounds, and other types. In general, in this mode, game roles and other games are represented by a set of unified cut graphs (the materials are segmented by a unified texture ), however, the number of map tiles occupied by game roles or game units is not fixed. For example, in the Lode Runner game, the game role occupies 1x1 tile.
In Toki Tori, the game role occupies 2x2 tile. As for flashback, because the background uses an uncommon small tile, the game role is even changed to two tile widths, and the height of five tile is increased when standing, 3 tile is high when the cursor falls.
In this type of games, game roles rarely move diagonal lines. However, if we break down the movement into two different directions, we can still do it. Similarly, it is very common to move only one pixel position each time a role moves. Continuous and fast movement can be achieved by moving the pixel position multiple times. The specific operation process for determining the movement is as follows:
1. First, copy the role location information in advance and place it into the desired location (for example, when moving to the right, copy one location to the right each time ).
2. Check whether the copy conflicts with the background.
3. If a collision is detected and the motion stops, the system responds.
4. Otherwise, you can pass. Move the role here. (You can add animations)
This kind of solution is very bad for the operation of the arc movement, so such games generally do not have this kind of movement, or only the upper and lower sides of the movement, because only the linear motion is taken into account here. The advantage is the simplicity and accuracy of this solution. Because the amount of information processed is very small, the number of game roles getting stuck rarely happens, and the controllability of the game is also very strong, and different situations do not even need to be fine-tuned.
Developing a game based on this mechanism is very simple. All you have to do is check the user's tile and the background's tile, and then align them and adapt to the actions you assign.
In principle, this mode certainly does not allow the existence of a role smaller than tile. However, we can also solve it in different ways. For example, the background tile can be set to be smaller than the tile of the game role, or you can perform visual reduction in a certain tile, but it does not affect the game logic. (This is probably the method taken by Lode Runner-The Legend Returns ).
Solution 2: Smooth tile Rendering
Generally, it is implemented by Constructing Dedicated tiemap objects and detecting specific role units of tilemap (many game engines have built-in tilemap classes or similar role classes ). Different from the previous scheme, In this mode, the role can move freely in the map, and the role coordinates are not restricted by the grid (if the number of moving pixels is smaller than 1 pixel, select an integer ). This is also a commonly used technique on 8-bit (such as FC) and 16-bit (such as MD) game machines in the past, and is also used today. It is easy to develop and is not too responsible for map editing. At the same time, it can also support slope and hop movements.
If you are not sure on which platform you want to develop the game, and you just want to make an action game, I suggest you adopt this solution. It is flexible and relatively easy to master. This also explains why most game developers use it.
Example: Super Mary, sonic the hedgehog, Mega Man, super Metroid, contra, Metal Slug
How to do it?
As far as the basic part is concerned, the acquisition of tile information in the map and the pure tile rendering method are the same. The difference is only the interaction algorithm between players and backgrounds. In tiemap, the collision unit of a game role is an axis-aligned box (the AABB algorithm can regard it as a rectangle that cannot be rotated ), it is usually equal to the integer multiple of a single tile. In general, it should be 1tile width, 1tile height (Little Mario), 2tile height (Big Mario), or even 3tile height. In many game examples, the role pasters are larger than the logical collision units for a more friendly visual effect and gaming experience (compared to preventing players from encountering unexpected places, it should also prevent him from touching what he should encounter ). For example, if your role texture is equal to or equal to two tile widths, in fact, the target to be collided is only one tile width.
Assuming there is no slope or single-direction platform, this algorithm is very direct:
1. Break down the action to the X and Y axes and move it once. I usually take the Y axis, but you can move a very large span in both directions.
2. Obtain the coordinates of the edge of the target object (forward. For example, if it is to move to the left, it is the X axis on the left of the collision unit. If it is right, it is right. Up is the above. Similarly, downward.
3. Calculate the overlap with the edge of the tile-get the straight of the smallest and largest orthogonal axes (for example, if we go left, the Orthogonal Axis is Y, then the player and 32, 33, 34 tile overlaps, so we can calculate the range of the texture in this direction: Y = 32 * ts, y = 33 * ts, y = 34 * ts, ts is the size of tile)
4. Before the role you control is moved, calculate the coordinates of the static state in the direction of movement and the coordinates of all the dynamic obstacles. How close is it to you.
5. The movable distance of the player in that direction will be the distance between the nearest obstacle and the minimum distance you want to move at the beginning.
6. Based on the above calculations, the new location of the game role after moving is obtained and the coordinates are updated.
Slope
Slope calculation (indicated by the Green Arrow) is usually tricky because although it is an obstacle, it allows players to partially move into its tile. In the same algorithm, moving the X axis will cause the Y axis to change. One solution is to have the tile record the Y value twice (the left and right sides are math. Floor (y )). Suppose () is the upper left corner of the coordinate, then the tile y on the left of the role is (), the one standing on the top is (), then (), and then (12, 15 ). Next, we will repeat the tiles above. Then there are steep slopes () and ).
The following systems allow any slope. The two slopes are the most common for sensory reasons, A total of 12 types of tile (6 previously described and their images ).
Collision algorithm (horizontal motion ):
* In the collision detection stage (step 4), the slope algorithm only counts players close to the high edge (small y value) as one collision, then the player will not see the role's "shoot" on the slope (trembling back and forth ).
* You may prohibit the slope from blocking "semi-Traversing". Many games like Mega Man X use this restriction. However, if you do not want to limit the performance, it will be complicated for players to climb from the lower of the upper. One way is to pre-process and mark these troublesome slopes. Then, in the collision detection phase, if the minimum value (bottom side) on the Y axis during the player's climb) is a value greater than the slope edge height on the Y axis (tile coord * tile size + floor y), even if a collision.
* After the collision is handled, the player can start to move (step 1), but the vertical position needs to be adjusted. When it goes uphill, this is relatively easy: Calculate the height of the value in the coordinate axis at the midpoint of the lower edge of the player collision box: First, find the fast Ground tile. If it is a oblique edge, calculate its floor Y in the current coordinate system. For example, t = (centx-tilex)/tilesize; floory = T * leftfloory + (1-T) * rightfloory. Move the player to find the lowest position (assuming that the player is fixed in the middle of the bottom edge of the texture, That's it ).
* Downhill is tricky. One way is to calculate the number of pixels on the ground before the player moves, and then the number of pixels after the player moves (using the formula above), and then adjust the height of the two pixels to make them the same. This method can be used for both upstream and downstream scenarios. The other method is to introduce gravity, but it is very troublesome.
* A non-slope obstacle tile should not be considered in collision detection if it is adjacent to the slope. If the player is in the (0, *) position and has a slope in the upper left to lower right, the tile on the left is ignored. If the slope is (*, 0) from the lower left to the upper right, ignore the slope on the right. If the player has more than two tile, ignore more tile. This is done to prevent players from getting stuck (in yellow) When climbing ).
* When going down, you do not need to consider the top edge of the Oblique Edge tile as the collision boundary. Instead, you need to calculate its current vertical floor coordinates (using the same algorithm ).
One-way platforms
Compared with Super Mario, this section shows the scenario where Mario falls down and stands on the same single-direction platform.
The so-calledOne-way platforms(Single-direction platform) is the platform that you can stand or jump through. Another saying is that if you stand on top, it will be an obstacle, otherwise it will be able to be crossed. This is the key to understanding such platforms. The algorithm also needs to be changed as follows:
* In the X direction, there will never be any obstacle
* In the y direction, if the player is already above the game, it is an obstacle. (The bottom edge of the player's collision box must be at least one pixel above the one-way platform ). Before moving, you must store the player location information to check the status.
Some people seem to think that when the player's Y axis speed is positive (down), it is a good idea to count one-way platforms as obstacles, but this idea is wrong. This is because players may overlap the platform during a jump, but they are not in touch with the platform when they fall. In this case, players should continue to fall.
Some people want to allow players to jump from such platforms. There are several relatively simple algorithms to solve this problem. For example, one-way platform is prohibited at a frame, then keep the player at a speed of at least one in the Y direction (then the player does not have to consider the specific game collision unit in the next frame collision detection ). Or check whether the player is on the top of the one-way platform. If yes, manually move the player down one pixel to achieve the effect of traversing the platform.
Ladders
Ladders (stairs)
The stairs in the game seem to require complex operations. But in fact, it is just the simplest if statement: when you are on the stairs, you can ignore most collision detection and use new rules to define it. Most people define a stair as only one tile width.
There are two ways to go to the stairs:
* Let your gamer collide with the box and the stairs overlap (for example, in the lgame, you can directly calculate whether the role's getrectbox has collided with the tile), either in the air or on the ground, then go up.
* Let your players stand on the top of the stairs, first deal with the aforementioned one-way platform method, and then go down.
Sometimes, these methods suddenly align the player's X-direction value with the stair tile. If you climb down the stairs, you can move the Y axis to put the players inside the stairs. At this time, some games use different collision boxes to determine that players are on the stairs. For example, Mega Man seems to use a single tile to do this.
There are also several ways to exit the stairs:
* When you arrive at the top of the stairs, an animation pushes players up several pixels.
* Reaching the bottom of the ladder produces a simple drop, although some games will not let players leave the stairs.
* Left or right. If no obstacle exists, it is allowed to pass.
* Skip. Some players are allowed to perform such actions in the stairs.
On the stairs, the player's actions also change. Generally, it can only move up or down, or perform some attacks.
Stairs
Stairs (Tier)
The ladder is a variant of the stairs and is well known in the castlevania series. The implementation method is actually very simple, basically the same as the stairs, but there are several exceptions that should be pointed out in advance:
* Players usually move one or half a tile.
* Each time a player moves the same distance between x and y
* The initial overlap check checks the previous tile instead of the current tile.
Some games also have steps like slopes. In fact, most of them only have visual effects.
Moving platforms
Moving platforms (mobile platform)
Mobile Platforms seem tricky, but they are actually quite simple. However, unlike the general platform, it is a moving platform and cannot be expressed by a fixed tile (in fact, most of the time a mobile platform is counted as a sprite )). Therefore, it is usually represented by a collision box (AABB. It is regarded as an obstacle in collision monitoring.
There are several ways to implement the mobile platform. An algorithm is as follows:
1. Before moving people and objects on the screen, determine whether the players are on the mobile platform. This can be done by checking whether the player's bottom edge point is exactly 1 pixel higher than the platform surface. If so, store a processing method on the platform and the player's current position.
2. Move all the mobile platforms to make them happen before the player moves.
3. For each role standing on the platform, calculate the translation volume of the platform, and then move each role to the same translation volume.
Other features
Some games have more complex features and are not as famous as the sonic the hedgehog series in this regard. These games are beyond the scope of the author's knowledge, so they will not be further explored (this implementation, you can use the polygon of lgame to create a shape and then use the tile map for collision calculation ).
Solution 3: pixel bit mask Calculation
The effect is similar to solution 2, but it does not compare the size of the tile and the game role when dealing with the collision relationship, but directly uses a whole picture as the background, then, the collision between each pixel in the background and the image pixel of the game role is checked, and the moving relationship is obtained.
This seemingly more detailed processing method also increases the complexity of the game and increases the memory usage. Likewise, there are high requirements for map editing (artist's nightmare ). Because tile cannot be used to create a view, it will lead to the number of units at each layer, which must be drawn by hand (a tough artist ~). Because of these problems, this method is not very common, but it will produce higher quality game results than tile. It is also suitable for dynamic scenarios, such as some destructive scenarios (for those who have enemies for artists, they can make such maps every day ~).
Example: worms, Talbot's Odyssey
How to do it?
In fact, the original idea of creating such a map is similar to the second solution: Think of each pixel as a tile, and then implement the same algorithm. However, this idea implies a major difference, that is, the handling of slopes. Because the current slope is implicitly defined by the surrounding tiles, the previous scheme is no longer suitable. Therefore, we also need to adopt more complex algorithms (In addition, stair processing will also become very tricky ).
Slopes
Slopes)
This solution is difficult to implement, largely because of the existence of slopes. Unfortunately, slopes are mandatory in many games.
The following roughly describes the slope algorithm of Talbot's Odyssey:
* The displacement of each axis is calculated through acceleration and vertical velocity.
* When the axis direction is moved, the direction with a large displacement is preferred.
* For vertical orientation, the collision unit (AABB) is moved up to three pixels, so as to achieve the goal of climbing.
* Scan the coordinates in advance and check the obstacles and bitmaps of all objects to determine the distance the player can move before encountering the obstacle. Then move the player to a new position.
* For vertical movement, players can move up to 3 pixels at most.
* If the movement ends, players are found to overlap with an obstacle. Cancel this move.
* Regardless of the result, move the preceding method in another direction.
This solution does not differentiate players who are moving downhill or falling down (in pixels). You only need to build a system to calculate the number of frames that have passed the last time the players were on the ground. In this way, once the judgment criteria are met, we can determine whether the role can jump or start an animation based on the judgment results. In Talbot, it is usually processed by Ten frames at a time.
Another trick is to calculate the pixels that can be moved before a collision occurs. However, some factors may make the calculation more complex, such as the one-way platform that passes through, to the steep slope of decline. In short, this technology requires a lot of tuning and is very unstable compared to the second solution. Therefore, unless you need it very much (for example, the artist's sister-_-), it is generally not recommended.
Solution 4: vector map
This technology uses vector data (line or polygon, typically using SVG, Scalable Vector Graphics) to determine the collision edge. Although it is difficult to implement, this solution is still very popular due to the popularity of physical engines. This scheme provides the advantages of the bitmask scheme, which consumes little memory computing and facilitates layer editing.
Example: braid and limbo
How to do it?
There are generally two solutions:
* Self-calculation collision. Similar to the bitmask solution (solution 3), polygon collision is used to calculate reflection or slide.
* Directly go to the game engine ......
Basically, most people choose the second method, which is not just a simple reason. More importantly, you can spend your thoughts and energy on other more important aspects. However, you need to note that if you only use the default settings for development, your game will be extremely similar to other games developed with the same engine and become uncharacteristic.
Compound objects
Compound objects (complex object)
In this solution, there is a very unique defect. It may suddenly fail to judge whether the player is on the ground (mostly for the reason of rounding) or hit the wall, or slide down through a slope. If you use a Game Engine (such as box2d), you also need to consider the friction, the movement is relatively large, the sliding is relatively small, and so on.
The general solution to deal with these situations is to divide the game role into several polygon. Each polygon represents different things: for example, you have a subject trunk, two slender legs, a square head, or even a combination of different polygon. Sometimes, we can use these small changes to prevent the role from getting stuck by obstacles. Most of the different components of game roles also have different physical attributes or collision responses. For more information, you can also use sensor monitoring. It is usually used to determine whether the treaty is too close to the ground, or whether the player is pushing the wall (hitting the wall repeatedly ).
Global considerations
No matter what type of 2D game development you are developing, you must consider some global factors.
Acceleration)
The acceleration of a role will have a significant impact on gamers.
Acceleration usually refers to the sudden change (Increase) of the speed of the game role. It mainly includes the actual increase of moving pixels and the short-term acceleration animation. When a game role is slow, it usually takes a long time to reach the maximum speed. However, in this case, the acceleration animation may be suspended halfway (if the player releases the button ). In this way, players may feel that the role is very inferior and difficult to control. When the role speed is very fast, it is easy to speed up to the maximum speed, but due to the relationship between animation changes, it may suddenly get stuck. It seems that the combination of animation and acceleration is very sensitive, and we should be very careful when coding.
Even if a game has no horizontal acceleration, it will at least add this type of factor to the jump. Otherwise, the jumping arc will become a triangle.
How to do it?
Acceleration is usually simple, but pay attention to the following:
* Pay attention to the speed above the X direction. If the control button is not pressed down, the acceleration value should be 0. (for specific settings, you can press the left button until the maximum negative speed is reached, on the right side is the largest normal speed ).
* Pay attention to the speed in the Y direction. It is 0 on the ground and reaches the maximum when it falls down.
* For each specific direction, you can use the weights of each direction to average or directly add the maximum speed after the current speed is accelerated.
The two acceleration methods are as follows:
* Average weight: acceleration is a number between 0 (not changed) and 1 (Instantaneous Acceleration. Use the straight-forward method to perform linear interpolation between the target speed and the current speed. Use the result as the current speed.
vector2f curSpeed = a * targetSpeed + (1-a) * targetSpeed;if (fabs(curSpeed.x) < threshold) curSpeed.x = 0;if (fabs(curSpeed.y) < threshold) curSpeed.y = 0;
* Acceleration: first determine the direction in which the acceleration is added (using the sin function), and then check whether there is too much acceleration.
vector2f direction = vector2f(sign(targetSpeed.x - curSpeed.x),sign(targetSpeed.y - curSpeed.y));curSpeed += acceleration * direction;if (sign(targetSpeed.x - curSpeed.x) != direction.x) curSpeed.x = targetSpeed.x;if (sign(targetSpeed.y - curSpeed.y) != direction.y) curSpeed.y = targetSpeed.y;
It is important to integrate acceleration into the role's speed before moving a role. Otherwise, you will find a frame lagging behind in the role's screen display. When an obstacle is encountered, the acceleration in the corresponding direction should be set to 0.
Jump Control
In a 2D flat game, you only need to check whether the player is on the ground (or whether the player is on the ground when the first few frames are on ). If so, set an initial negative Y speed (or physical "Driving Force") for the role and then let gravity do the rest.
Generally, players have four ways to control the jump:
* Driving force: This is often seen in Super Mario games. A role can be moved before it jumps. In some games, this is the only method that can affect the Skip arc, just like in real life. You don't actually need to implement anything unless you forcibly intervene to stop it.
* Air acceleration: horizontal movement control is maintained during the air. Although it is impossible to implement it in real life, this feature is very popular in the game, which makes it easier for players to control the role. Almost all flat games have this feature, but Prince Persian is a special character. Generally, acceleration in the air is reduced very quickly, so the driving force is very important, but some games (Mega Man) will provide powerful air control. This is just to slightly modify the acceleration parameters in the air.
* Rise control: another activity that cannot be achieved by material resources is also very popular. The longer you press the jump button, the higher you can jump. This feature can be achieved by constantly adding the driving force to the role (the driving force size is a decreasing trend), or by overcoming the influence of gravity. However, there is a time limit for such a supplement. Otherwise, you will see your role "flying sky" (the main character line of game king zexal ).
* Multiple jumps: once in the air, some games allow gamers to jump again, or even jump unlimitedly until the players reach the ground (two jumps are generally adopted by everyone ). This method can be achieved by adding a counter inside the game. This counter is automatically incremented by 1 during each hop, and then automatically becomes 0 after it reaches the ground. Skip can continue only when the counter does not exceed the limit value. The second hop is usually much shorter than the first one. Another restriction is that an air jump can be triggered only when the role completes the rotation jump and begins to fall back.
Animations and leading
In many games, your role executes a preset animation before actually executing your commands. If you have a high requirement on operations in a game, this may make your players crazy. Therefore, do not do this in such games. However, you can still add a wizard animation (before jumping or running ). If you care about the response speed of the game, you 'd better just describe it and immediately let the role respond to the command.
Smoother movement with pseudo-float coordinates (smoother movement)
Compared with floating point computing, it is wise to use an integer to represent the player's position, because this will make computing faster and more stable. However, if you shape everything, you will find that the movement is very slow (because all locations are rounded up ). There are some solutions for this situation:
* It is very easy to use floating point data to calculate and store location information and convert it into an integer when computing and performance collide. However, if you start to move from the origin, you will find that the deviation is very large. However, if your scenario is very small, the difference is not that obvious. But remember, if you do, you can use double precision for calculation and correction.
* Positions are calculated using fixed-length numbers and converted into integers during collision. Although the scenario size limit is greater, the accuracy is globally unified and faster on some hardware (such as mobile phones ).
* Store the location information as an integer and convert the number after the decimal point to the floating point type. When calculating the unknown position, calculate the displacement by floating point, add the number after the decimal point, add the integer part to the unknown integer, and add the fractional part to the unknown decimal point. The calculation is the same in the next frame. The advantage of doing so is that you only use the floating point type when moving, while the rest are integers, which can improve the overall performance.