3D in GameMaker can be tricky to set up if you’re only reading the manual. In this tutorial I’ll show you how I use Surfaces and basic 3D functions to create a background for my shmup style game. If you’re new to GameMaker, make sure you have a good understanding of it’s interface and how to use GML first.
The first step is to import your textures as a background, making sure the resolution is a power of 2 (32, 64, 128, etc…) then tick the “Used for 3D” check box.
Creation Code
Create an object and give it a depth of 10,000. Give the object a Create Event and add this code:
surf_3d = noone; w = room_width; h = room_height; direction = 270; zdirection = 35; x = 0; y = 0; z = 512; ratio = w / h; fov = 45; // Textures layer0 = background_get_texture(bg_floor); layer1 = background_get_texture(bg_wall);
I’ll give you a run down on what we’re doing in this code. surf_3d is going to be the surface. Because surfaces are volatile we’re defining it as nothing for now as we don’t need it until the draw event. w and h will be the width and height of the surface we’re drawing to. If you don’t want these to be the room_width and room_height then set them as you wish.
You’ll already be familiar with direction, x and y as we use them in 2D but for 3D we’re going to need zdirection and z variables. These aren’t built in to GameMaker but all that means is we need to define them. Direction acts as the cameras Yaw and zdirection acts as the Pitch. x, y and z define the point in 3D space that the camera is located.
Ratio is the aspect ratio it’s going to render in. You want the surface width divided by the height in this variable. I’m using a narrow FOV (field of view) because of my play field is narrow but you may want to increase it. Replace the texture names with the names you’re using. I’ll be showing you how to do walls and also floors in this tutorial.
Draw Code
Now it’s time to give the object we’re working in a Draw Event with code. This is what I’m using but you’ll want to work out your own code below the comment “Draw Stuff in 3D”.
if !surface_exists(surf_3d) { surf_3d = surface_create( w, h ); } surface_set_target(surf_3d); draw_clear_alpha( c_black, 1 ); draw_set_color(c_white); d3d_start(); // Camera xto = x + lengthdir_x(180, direction) * lengthdir_x(1, zdirection); yto = y + lengthdir_y(180, direction) * lengthdir_x(1, zdirection); zto = z + lengthdir_y(180, zdirection); d3d_set_projection_ext( x, y, z, xto, yto, zto, 0, 0, 1, fov, ratio, 1, 32000 ); // Draw Stuff in 3D d3d_draw_wall( -512, 0, 512, -512, 512*40, 0, layer1, 40, 1 ); d3d_draw_wall( 512, 0, 512, 512, 512*40, 0, layer1, 40, 1 ); d3d_draw_floor( -512, 0, -5, 512, 1024*20, -5, layer0, 1, 20 ); // 3D END d3d_end(); surface_reset_target(); draw_surface( surf_3d, 0, 0);
You guessed it, it’s run down time again.
Surface Code
The first task for the computer is checking if the surf_3d surface exists and if it doesn’t, it creates one with the width and height we’ve saved in w and h variables. This is why in the create event we set the surf_3d variable to equal noone. We need this code because surfaces are volatile and actions such as minimize the game will destroy the surface, causing a crash if the surface isn’t recreated. We then set the surface target to surf_3d so that we’re not drawing to the default application surface. The “draw_clear_alpha( c_black, 1 );” function clears the surface with opaque black. I don’t quite understand why but if I don’t put this in it just renders a black image.
Camera Setup
Now we’re on to the 3D code. “d3d_start();” tells GameMaker we’re going to draw in 3D from now until we do “d3d_end();”.
While we’re using x, y, z to define where the camera is, we need to define another point for the camera to point towards. I’m using the variables xto, yto and zto for this. If you know how the “lengthdir_x” and “lengthdir_y” functions work, this isn’t too hard to decode the workings of.
“d3d_set_projection_ext” is the camera. We’ve got a lot of arguments on this function but most are self explanatory. The first 3 are x, y, z followed by xto, yto, zto and then we have xup, yup, zup. The last 3 determine what is “up” relative to the camera. Keeping this as 0, 0, 1 is best unless you want to work out how to roll the camera. Next we have “angle” (FOV), aspect (ratio), near clipping and finally far clipping distance. Near clipping is how close something has to be to the camera for it to clip inside and far clipping is draw distance.
Drawing in 3D
I’ll cover “d3d_draw_wall” and “d3d_draw_floor” here as they are very similar to use. First is 6 position arguments, then the texture to use (we set it in the create event), horizontal repeat count and vertical repeat count. With the x, y and z positions you can imagine yourself looking down on the scene. The x and y are the same as 2D from that perspective and you just add z for how high you want. It’s tedious but really quite simple when you play around with it.
Ending 3D and Drawing the Surface
It’s time for “d3d_end();” to end the 3D drawing. We then need to reset the surface target, meaning it’ll be drawing to the default application surface again. The last line is drawing our image to the application surface. You can change the x and y if you have a boarder offset.
Conclusion
You can play with the x, y, z, direction and zdirection and see what they all do. If you want a forward moving camera, adding to the y and resetting it is useful. For example:
y += 16; if y > 2048 { y -= 2048; }
This code will move you forward and seamlessly knock the camera back if your background lines up just right (my example code will).
If this was your first time using 3D in GameMaker then I hope you’ve gained a good understanding of how to play around with it. GameMaker isn’t designed for high quality 3D scenes but with some trial and error you can certainly make nice backgrounds for 2D games.
If you have any trouble please post a comment and I’ll try to help.
This Post Has 14 Comments
Simply Awesome.
Thank you!
Thanks for this useful tutorial keep doing them they’re amazing 😉
Thank you! Many more to come. 🙂
Thanks for this great tutorial! I can see the dedication and effort you put in these tutorials. I really admire you. I’m sorry to bother you but I would really like to ask you some help. It’s about the spawning of enemies. I’ve been trying to do it but every time I try I get something really bad…I don’t know what to do to do it properly… So umm I was wondering if you could make a very simple tutorial (if you don’t mind, of course) I would really appreciate it… Well thank you for reading this Bye!
Hi Darky202,
Thank you! I may be able to do one on how I spawn enemies in my Shmup if you’re interested. What kind of game are you making?
Thanks for replying! Well that’s a very funny story… I noticed that there are no many alternatives to play Touhou in Linux than Wine or a VM…I just wanted to change that by making a Touhou-like game on GMStudio and export it to a .deb file after finishing it (Maybe it sounds a little dumb…) It’s been a week since I started this project so umm yeah It’s kinda serious…Being honest at first I was completely lost with the coding stuff but I think I’m having a good progress (Now even more because I found your awesome tutorials) so yeah that’s it… I think. I’m so happy you answered! Thank you! (Also…sorry for my bad English )
Hey John, fantastic tutorial! It was really helpful, but in my game, it creates a certain problem for some reason. I’ve spent days on trying to fix that and I’d be really glad if you could help me out a bit. So, the problem is this: https://imgur.com/WCO1vgJ
The y-axis gets flipped upside down once 3D is activated, but only for objects and text that are not drawn on the GUI layer (the buttons at the bottom are drawn on the GUI layer.) I guess the reason for that is that gamemaker’s y-axis is inverted, and once the y-axis becomes the z-axis, the vertical axis isn’t inverted anymore, and makes everything appear flipped upside down. Do you know where or how to change that? Leaving out the d3d_set_projection_ext function fixes the issue, but basically also deactivates 3d, so it makes the object useless. Thanks for your time.
Hi Robert,
I’ve been trying to recreate your problem but haven’t had any luck in doing so.
At the end of the article I have a link to the GMZ, does it have the same problem for you?
Let me know how you go with it, I’m curious as to what the problem is!
Best of luck,
John
Hey John,
thanks for your answer! I downloaded the gmz, and when starting the project in GMS 1.4, it all worked as in your video. But when I imported the project into GMS 2, everything got flipped upside down. Seems like GMS2 is at fault. That’s actually a good information, maybe I should try everything you did with those new GMS 2 functions.
Thanks for helping,
Robert
I had a good look through my GMS2 3d code (it’s quite different than GMS1) and found the “zup” is flipped so you need to use -1.
https://i.imgur.com/OAbIlt7.png
I hope this fixes it for you.
This is better than the actual Game Maker “official” tutorial.
Thanks a lot.
Thank you!
Looks awesome, but is unfortunately outdated. Got anything similar for GMS 2.3.3?