3D for GML: Bullet paths
|This page is a work in progress. This tutorial has not been tested on ENIGMA. Helping us by making the code compatible is appreciated.|
22.1 Animating explosions
When you create a 2D game, GM makes it really easy to create aiming and shooting because directions and collisions are implemented, even in the standard drag and drop library. When switching to 3D games, it’s not that easy. Let’s walk through each script of the gm6 that comes with this text. First in the list is the script ani_explosion, an animation script for an explosion, holding this code:
if ani>32 then instance_destroy();
A value called ani is increased and when it reaches a certain value the instance is destroyed. This script is used to show an explosionthat grows in size and then disappears.
The next script in line is ‘draw_camera’, a drawing script for the camera object, holding the following code:
xf=obj_character.x; //x to look from
yf=obj_character.y; //y to look from
zf=obj_character.z+16; //z to look from
xt=xf+cos(degtorad(direction)); //x to look to (with direction)
yt=yf-sin(degtorad(direction)); //y to look to (with direction)
zt=zf-sin(degtorad(zdirection)); //z to look to (with z direction)
d3d_set_projection_ext(xf, yf, zf, xt, yt, zt, 0, 0, 1, 70-mouse_check_button(mb_right)*60, 1, 1, 1024); //look from & to
This script will look familiar if you have studied previous tutorials. The last line is interesting though as it checks the right mouse button. If the right mouse button is pressed, the camera zooms in. In other words, holding the right mouse button activates sniper zoom mode.
22.3 Drawing an explosion
The next script is called draw_explosion, which draws the explosion. Here’s the code that is used:
d3d_draw_ellipsoid(x-ani, y-ani, z-ani, x+ani, y+ani, z+ani, background_get_texture(bk_explosion), ani/16, ani/16, 4+ani/2);
As you can see, the alpha value is reduced as the value ani is increased. This means the explosion will gradually dissolve. Other effects are realized through creative use of the value ani. The alpha value is reset at the end.
22.4 Drawing patches of floor
In the next script, draw_floor, you can see that each one of the patches that make up the floor is drawn at an appropriate depth. For the aiming and bullets this is not very relevant so let us move on.
d3d_draw_floor(x-32, y+32, 0, x+32, y-32, 0, background_get_texture(bk_floor), 1, 1);
22.5 Targeting crosshairs
The next script, ‘draw_overlay’, draws an overlay (or HUD, if you like) of crosshairs, two black lines crossing each other in the center of the screen:
draw_line(320-32, 240, 320+32, 240);
draw_line(320, 240-32, 320, 240+32);
This is probably the easiest and fastest way to create a targeting cross.
22.6 Marking the path
The next script, ‘draw_path_marker’, is a drawing script for the marker of the bullet path. You don’t have to draw the path of the bullet in the game but it helps in testing or understanding how things are actually done. This isthe code used:
You can check the Visible checkbox in the Object Properties if you want to display the path markers. A path marker is used to mark the 3D path of the bullet and check for collisions. More about that later.
22.7 Drawing a sky dome
The next script, draw_sky, guess what? Yes, it draws the sky. Let’s move on because there is not much to see here.
d3d_draw_ellipsoid(-1024, -1024, -1024-64, 1024, 1024, 1024-64, background_get_texture(bk_sky), 2, 1, 32);
22.8 Drawing facing walls
The next script, draw_wall, draws the walls. We have four types of walls, one for each direction. As you can see, a type value is used to determine the type of wall that needs to be drawn:
//set depth based on camera
//check wall types!
if type=0 then //facing north
d3d_draw_wall(x-32, y, 0, x+32, y, 128, background_get_texture(bk_wall), 1, 1);
else if type=1 then //facing east
d3d_draw_wall(x, y-32, 0, x, y+32, 128, background_get_texture(bk_wall), 1, 1);
else if type=2 then //facing south
d3d_draw_wall(x+32, y, 0, x-32, y, 128, background_get_texture(bk_wall), 1, 1);
else if type=3 then //facing west
d3d_draw_wall(x, y+32, 0, x, y-32, 128, background_get_texture(bk_wall), 1, 1);
22.9 Setting up a bullet
In the next script, ini_Bullet, a bullet is initialised (set up). This is where the good stuff is. Look at the code that is used:
xt = x+anglex*distance;
yt = y+angley*distance;
zt = z+14+anglez*distance;
if zt<1 then
use=instance_create(xt, yt, obj_explosion);
use=instance_create(xt, yt, obj_path_marker);
The angle of the bullet trajectory (it's path) is calculated based on the direction and z direction of the camera object. In other words, the bullet travels in the direction you are looking in, which is what you want.
22.10 Placing markers on a bullet path
Now that we have the angle at which the bullet needs to travel, we can place the path markers at intervals. The use of path markers is not the fastest way but it is easy to understand because it uses objects. And objects is what you have been using since the day you started with 2D games in GM. We see a loop, starting with this line of code:
This loop is used to walk along the path of the bullet, so to speak; it starts at a small distance from the player (distance=2) and ends at a large distance from the player (distance<1024), with steps of 8 units each (distance+=8). Each step, the code that follows is executed, starting with these three lines:
The three points (xt, yt, zt) determine the x, y and z of the path marker object. Each time, one of these path markers is placed at a distance from the shooter. The angle values that were determined based on the camera’s direction (remember?) are used to calculate the exact points to place the path markers. The next line makes sure that no marker is placed if the marker is too close to the ground or below the ground.
No marker is placed. In stead an explosion is placed immediately, without even creating a marker at that spot. It also exits this script because the bullet has already reach its end destination. The script continues with these lines:
As you can see, a path marker object is placed at the location and its z value is set. This script ends with the line that plays the sound of firing: sound_play(snd_fire);
22.11 Checking for hits
The next three scripts (init_camera, init_character, and init_explosion) do not hold any surprises, especially not if you are familiar with these tutorials. In the next script, ini_hit_wall, we see what happens when a path marker collides with a wall object:
What this does is: create an explosion when the marker is below a z value of 128 units (which is the height of the buildings).
22.12 Understanding bullet paths
The rest of the scripts should not hold any surprises. So, let’s conclude by trying to understand the general principles behind bullet paths that we have seen in this
walkthrough. When the player presses the left mouse button, a script to make a bullet path is run from the Character object. The exact 3D angle is calculated and path markers placed at intervals. When a marker collides with a building block (or when a marker is below ground level), an explosion is created.
All rights reserved. Copyright © 2004 by John J.A.H. Weeren. Unauthorized use or reproduction, whole or in part, without written permission from the author is strictly prohibited. This page is part of the tutorial 3D For GML. Reproduced with permission. For more information, please see the talk page.