3D [SOLVED] How to invert Y axis in 3D top down?

ForesterPL

Member
Hello everyone!
I want to create a 2D game with fake 3D top down perspective, something like GTA 1. I started to look for tutorials and I found this:


But in my game, it looks like this:


I'm not satisfied. Can anyone help me with code? I want more realistic fake perspective.

Please, give me a tips, some examples with explanation etc.
Thank you in advance!
 

nesrocks

Member
Your game looks exactly like the tutorial. It seems to me it is a matter of texture work and uv mapping to make it look good.

edit 1: To be honest though, the perspective looks inverted. The top of the buildings should be bigger than their base, not smaller. In GTA (1) it works like this, the top is bigger.
edit 2: Actually, each building has its own orthographic projection in this tutorial. No wonder it looks weird... I suggest looking for another tutorial entirely.
edit 3: I kiiiind of got it working by editting these lines in the building_draw event, but it needs a lot more debugging and trial and error than what I'm willing to invest into this. Personally I'd just do proper 3d models.
Code:
if yoff>0draw_sprite_warp(sprite_get_texture(building_sprite,1),xstart-(sprite_height/2),ystart-(sprite_height/2),xstart+(sprite_width/2),ystart-(sprite_height/2),0.98*(xstart-(sprite_width/2)+xoff),0.98*(ystart-(sprite_height/2)+yoff),1.02*(xstart+(sprite_width/2)+xoff),0.98*(ystart-(sprite_height/2)+yoff),stepud)
 
Last edited:

ForesterPL

Member
Your game looks exactly like the tutorial. It seems to me it is a matter of texture work and uv mapping to make it look good.

edit 1: To be honest though, the perspective looks inverted. The top of the buildings should be bigger than their base, not smaller. In GTA (1) it works like this, the top is bigger.
edit 2: Actually, each building has its own orthographic projection in this tutorial. No wonder it looks weird... I suggest looking for another tutorial entirely.
edit 3: I kiiiind of got it working by editting these lines in the building_draw event, but it needs a lot more debugging and trial and error than what I'm willing to invest into this. Personally I'd just do proper 3d models.
Code:
if yoff>0draw_sprite_warp(sprite_get_texture(building_sprite,1),xstart-(sprite_height/2),ystart-(sprite_height/2),xstart+(sprite_width/2),ystart-(sprite_height/2),0.98*(xstart-(sprite_width/2)+xoff),0.98*(ystart-(sprite_height/2)+yoff),1.02*(xstart+(sprite_width/2)+xoff),0.98*(ystart-(sprite_height/2)+yoff),stepud)
It still doesn't work. :/ I also tried something with d3d functions, but I totally messed game.
 

ForesterPL

Member
I'm pretty confused now. But anyway, correct me if I'm wrong:

1. I should enable perspective with d3d_set_perspective(...) and d3d_set_projection_perspective(...);
2. I should draw my building with d3d_draw_block(...);
 

Joe Ellis

Member
are you doing "d3d_start()" at the start of the game?

you'd also need to set the projection at the start of the draw event

heres an example which should follow a player object around with a topdown view:

d3d_set_projection_ext(x, y, 1000, x, y, 0, 0, 1, 0, 60, screen_width \ screen_height, 1, 5000)

put that in the player's "draw begin" event

then for the buildings you can do d3d_draw_block in their draw events, with whatever x y coordinates you want, and the z1 coord should be the height of the building, and z2 the base\bottom of it

there is also a problem how a d3d_draw_block can only have 1 texture, so you'd probably need to make the buildings out of d3d_draw_walls and d3d_draw_floors with the separate textures,

it should be noted that all of these d3d_draw functions are incredibly slow compared to the rest of gamemaker's draw functions, once you have about 40-50 buildings things will get real slow,

This will be ok for a start while you get used to 3d, but you should end up learning about vertex buffers, as these are alot faster and lets you render millions of 3d triangles at a time with good frame rate
the fastest solution I've found is building the entire level (buildings, roads, all static objects) into 1 vertex buffer per texture
 
Last edited:

Nux

Member
I believe d3d makes use of object depth for its perspective projection (correct me if I'm wrong; I've never used it), so you may have to make all your resources of similar depth. Imagine it like a z-axis.
 

ForesterPL

Member
I tried with depth, but it does not work in 100%... Also mouse does not work properly. I've never used d3d functions before, so I have bigger issues, than I expected. I don't want to give up. I will try to do something myself. If this won't work, then I will change it into "isometric perspective".
 

Joe Ellis

Member
why are the car's so much bigger than the road when perspective is on? the cars should have a z coord of a few units above the road,

also dont use depth for 3d, its not for 3d, its for ordering stuff when in 2d, when in 3d mode , everything depends on the z coordinates

also the mouse wont work properly at all while in 3d mode, you'd need to make a 3d mouse system,
however it will work in the draw_gui_event, cus in this event everything is drawn in 2d mode, unless you set a 3d projection in it

the z2(bottom) coordinate of the block should be 5, (5 pixels below the cars), aswell as the road, and the z1 (top) should be in negative coordinates, z is the same as the y coordinate in 2d, the z is the up \ down when in 3d, if you want something to move upwards you subtract
 
Last edited:
If you are just drawing sprites in objects, the depth will affect how 'high or low' it appears in the d3d projection. I am not real sure how you are drawing everything, though - so that may not help ;)
 

Joe Ellis

Member
the depth will affect how 'high or low' it appears in the d3d projection
oh yeah sorry i forgot about that, yeah the depth will be the same for sprites as a z coord with a 3d object
 
Last edited:

ForesterPL

Member
OKAY! I'm very, very very close to the solution.
I used engine from here: https://marketplace.yoyogames.com/assets/1873/basic-fake-3d

In obj_building I have got:
Create Event:
Code:
/// SET UP VARIABLES

// MAXIMUM HEIGHT OF THE CUBOID
max_depth = 4096;

// OBJECT TO "FOLLOW"
obj_to_follow = obj_player;

// A FACTOR TO "SLOW DOWN" THE EFFECT, CHANGE THIS TO MAKE THE EFFECT MORE DRASTIC
factor = 2;

// INITIALISE OTHER VARIABLES
hdepth = 0;
vdepth = 0;
depth = -y;

Step Event:
Code:
/// CALCULATE DEPTH AND STUFF

// SET DEPTH BASED ON THE POSITION OF THE FOLLOWED OBJECT
hdepth = (obj_to_follow.x - x) / factor;
vdepth = (obj_to_follow.y - y) / factor;

// CHECK DEPTH OF CUBE AND ALTER VARIABLES USED WHEN DRAWING
if (hdepth > max_depth / 2)
{
    hdepth = max_depth / 2;
}

if (hdepth < -max_depth / 2)
{
    hdepth = -max_depth / 2;
}

if (vdepth > max_depth / 2)
{
    vdepth=max_depth / 2;
}

if (vdepth < -max_depth / 2)
{
    vdepth = -max_depth / 2;
}

// SET DRAW DEPTH TO MAKE OBJECTS CLOSER TO THE BOTTOM OF THE SCREEN DRAW LAST
depth = -y;

Draw Event:
Code:
/// DRAW THE BOTTOM AND THE SIDES IN THE DRAW EVENT
//  SO THAT THEY WILL NOT COVER THE TOP OF OTHER CUBES

draw_self();

if (obj_player.y < y)
{
// TOP
draw_sprite_pos(spr_wall, 0, x - hdepth, y - vdepth, x + 128 - hdepth, y - vdepth, x + 128, y, x, y, 1);
}

if (obj_player.x > x)
{
// RIGHT
draw_sprite_pos(spr_wall, 0, x + 128, y, x + 128 - hdepth, y - vdepth, x + 128 - hdepth, y + 128 - vdepth, x + 128, y + 128, 1);
}

if (obj_player.y > y)
{
// BOTTOM
draw_sprite_pos(spr_wall, 0, x - hdepth, y + 128 - vdepth, x + 128 - hdepth, y + 128 - vdepth, x + 128, y + 128, x, y + 128, 1);
}

if (obj_player.x < x)
{
// LEFT
draw_sprite_pos(spr_wall, 0, x, y, x - hdepth, y - vdepth, x - hdepth, y + 128 - vdepth, x, y + 128, 1);
}

Draw End Event:
Code:
/// DRAW THE TOP OF THE CUBE IN DRAW END SO IT WON'T BE COVERED

// TOP
draw_sprite_pos(spr_top, 0, x - hdepth, y - vdepth, x + 128 - hdepth, y - vdepth, x + 128 - hdepth, y + 128 - vdepth, x - hdepth, y + 128 - vdepth, 1);

And in game it looks:
1.png

Every sprite is 128px*128px.
Walls are centered at 128px*128px.
Roof is centered at 64px*64px.

First I want to move walls and roof to these points:
2.png

PROBABLY next is to replace red points with blue, like this:
3.png

I'm not sure, but I think after that everything should look like this:
4.png
I just want this effect ^
If I'm wrong somewhere, correct me.
I'm counting on your help!
 
W

Wadlo

Guest
Here's a suggestion for you to get the 3d effect you want. I looked through the code, and it appears that changing a couple variables makes the 3d more intense. Try experimenting with max_depth and factor.
I changed max_depth to something huge like 140000 and it gave the game more 3d. Also, changing factor to something like 1.8 did something similar.

Another note, I just copied all your code into an example file, and I am getting the effect you desire. You need to make it so the roof is centered at 0, 0 in order for it to not move over a ton. That being said, the player's position would do 3d based on the corner of the building and not the center...

Anyway, good luck with everything. Hopefully something in my rambling helps.
 
W

Wadlo

Guest
Hold on, I was just reading through it again, and it actually looks like there is no attempt at making perspective exist. Give me a few minutes and I'll figure it out.
 
W

Wadlo

Guest
Here's a start.

Code:
//Draw event

depth = -y;
addedDepth = 50

if (obj_player.y < y)
{
// TOP
draw_sprite_pos(spr_wall, 0, x - hdepth - addedDepth, y - vdepth - addedDepth, x + 128 - hdepth + addedDepth, y - vdepth - addedDepth, x + 128, y, x, y, 1);
}

if (obj_player.x > x)
{
// RIGHT
draw_sprite_pos(spr_wall, 0, x + 128, y, x + 128 - hdepth + addedDepth, y - vdepth - addedDepth, x + 128 - hdepth + addedDepth, y + 128 - vdepth + addedDepth, x + 128, y + 128, 1);
}

if (obj_player.y > y)
{
// BOTTOM
draw_sprite_pos(spr_wall, 0, x - hdepth - addedDepth, y + 128 - vdepth + addedDepth, x + 128 - hdepth + addedDepth, y + 128 - vdepth + addedDepth, x + 128, y + 128, x, y + 128, 1);
}

if (obj_player.x < x)
{
// LEFT
draw_sprite_pos(spr_wall, 0, x, y, x - hdepth - addedDepth, y - vdepth - addedDepth, x - hdepth - addedDepth, y + 128 - vdepth + addedDepth, x, y + 128, 1);
}

//This might be upsidedown. You'll have to swap it and figure it out.
draw_sprite_pos(spr_roof,0,x-hdepth-addedDepth,y - vdepth-addedDepth,x-hdepth+addedDepth+128,y-vdepth-addedDepth,x-hdepth+addedDepth+128,y-vdepth+addedDepth+128,x-hdepth-addedDepth,y-vdepth+addedDepth+128,1);
If you change the variable addedDepth, you can adjust how 3d you want the buildings to be. (50 might be a little much)
There is one problem, and that is that you will need to change the order that the walls (Left, right, up, down) are drawn. You'll see that because there is now a perspective, the right and left wall will draw on top of the top wall in certain cases.

I might also add that you will have to rewrite your depth code to be based on distance to the player rather than -y. something like depth = point_distance(x,y,obj_player.x,obj_player.y).

Good luck! Again, let me know how it goes.

upload_2017-12-30_9-21-46.png
 
If I were you, I'd draw the buildings in proper 3d. That distortion on the texture is caused by the way textures are interpolated, if the building was 3d, that problem would go away.
 

ForesterPL

Member
If I were you, I'd draw the buildings in proper 3d. That distortion on the texture is caused by the way textures are interpolated, if the building was 3d, that problem would go away.
But if I will do in 3d, it will be a lot of messing with objects' depths, new 3d cursor system etc. I guess.
 

ForesterPL

Member
So... Is it possible to make a 3d building on 2d without any another big messing in code?
I mean to easily draw 3d block and don't mess with every objects' depth etc.
I'm totally new with 3d in GameMaker, so if you can, give me some explantaion, please.
 
Last edited:

ForesterPL

Member
But is it possible to make it without using d3d_start(); ?
If not, then I will have to make a new 3d mouse system (I guess) :(
 

Joe Ellis

Member
The mouse system isnt that hard, it just involves making a 3d line from the mouse that goes through the 3d world and "intersects" the objects (to tell what the mouse is over), I've got a couple of scripts for this if you need it
 

ForesterPL

Member
I took a short break from developing the game because I had a lot of learning, things at school, etc.
I come back to this game again, and I don't know where to start now.

First:
Will drawing 3D buildings on 2D cause depth problems?

Second:
Is the solution easier than I think? ;-;
 

ForesterPL

Member
In obj_camera Create Event I used:
Code:
d3d_start();
d3d_set_perspective(true);
d3d_set_projection_perspective(0, 0, room_width, room_height, 0);
In obj_building Draw Event i used d3d_draw_block(...);
It looks great, but... Everything is "mirrored".
In room editor it looks:
Bez nazwy.png

But in game:
Bez nazwy.png

What happened to instances' positions?
 
Last edited:

ForesterPL

Member
Also when I'm driving a car, and I want to turn right, it turns left... And if it is need to turn left, it turns right. It's same with cursor; Y coordinate is inverted.
 
Last edited:

icuurd12b42

TMC Founder
GMC Elder
try
d3d_set_projection_perspective(0,room_height, room_width, -room_height);

this will flip the projection around on y

>In obj_camera Create Event I used:
...

BTW your 3d init stuff should go in a Cam object, in the draw. with a really deep dept so it draw first, setting everything right every draw
 

ForesterPL

Member
try
d3d_set_projection_perspective(0,room_height, room_width, -room_height);

this will flip the projection around on y

>In obj_camera Create Event I used:
...

BTW your 3d init stuff should go in a Cam object, in the draw. with a really deep dept so it draw first, setting everything right every draw
I put everything in Draw Event, changed depth to -999999, and it's still same.
 

icuurd12b42

TMC Founder
GMC Elder
the method I posted with the changes in the region to set the projection to should work. The region is inverted on Y by saying the region starts from the bottom and has a height of negative height... you may need to put in the view height instead of room height if you are using views
 

ForesterPL

Member
Code:
y = -y
I tried this at the beginning. And nothing.

the method I posted with the changes in the region to set the projection to should work. The region is inverted on Y by saying the region starts from the bottom and has a height of negative height... you may need to put in the view height instead of room height if you are using views
d3d_set_projection_perspective(0, view_hview[0], room_width, -view_hview[0], 0);
Still nothing (or I just made a mistake...)
 

ForesterPL

Member
I just realized:
Part of the map, where player starts is over room_height...
Bez nazwy.png

I tried also this:
Code:
d3d_set_projection_perspective(view_xview[0], view_hview[0], view_wview[0], -view_yview[0], 0);
It's not perfect yet, but Y axis is working properly!
Bez nazwy.png
I should check objects' depths, I guess.
 

icuurd12b42

TMC Founder
GMC Elder
try
d3d_set_projection_perspective(view_xview[0], view_yview[0]+view_hview[0], view_wview[0], -view_hview[0], 0);

you had the last param set to -yview... be careful

as for things that may seem of hover above where they should like the car there, set the depth of the car to the same as the road... depth allows fractions too so if the road is at depth 0, set the car's depth to -.01, just slightly above
 
Last edited:

ForesterPL

Member
try
d3d_set_projection_perspective(view_xview[0], view_yview[0]+view_hview[0], view_wview[0], -view_hview[0], 0);

you had the last param set to -yview... be careful

as for things that may seem of hover above where they should like the car there, set the depth of the car to the same as the road... depth allows fractions too so if the road is at depth 0, set the car's depth to -.01, just slightly above
Aw! Finally everything is as it should be! Thank You very much!!
 
Top