Dungeon Warping via Orthographic Projections

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello whilst I was planning future videos it became apparent to me that I need some sort of base gain platform in order to demonstrate some of the algorithms that I want to talk about and so this video describes this very simple platform that I've developed and as you know on this channel I'm a bit of a fan of the retro aesthetic and so I've gone for the style of the 16-bit role-playing game and I've got a whole series dedicated to coding your own role-playing game and I'll put a little link above however this video specifically isn't about developing a role-playing game it's about establishing this platform and I also want to show how you can use and abuse warped decal rendering in the pixel game engine and so today we're going to develop this little editor and you might think that this is quite a simple thing and it is really you can move the cursor around you can place a new block place a few and you can hold down the tab key and choose from a particular texture what you want on the block so far so good but so what I hear you cry we've done stuff like this plenty of times before on the channel well of course there's a small twist and quite literally we can rotate our world to any angle we like because I always liked the idea when I was playing back on my Super Nintendo the possibility of being able to rotate the worlds to find hidden doors and new pathways and things and we can arbitrarily rotate to any given angle and it doesn't matter what the angle is it retains the two-dimensional aesthetic and that's because I'm not using perspective transformations I'm using orthographic transformations and we'll look at that a little bit later as well as being able to adjust the camera angle we can also adjust the cameras tilt now certain tilts will start to make it look a bit strange because our eyes are not traditionally used to looking at orthographic transforms and the way I've structured this application means we can actually have very large worlds fundamentally even though the world looks three-dimensional it is in fact a two-dimensional grid and this means we can apply lots of the techniques that I've described in videos before and we will see in this particular video that even though I've got a previous series on creating a 3d graphics engine from scratch we're actually not really going to rely on very many 3d techniques there are some but we don't need to delve into the full matrix stack and transformations like we normally would the way I can draw 3d graphics in this sense is by using the draw warped decal function provided by the pixel game engine in fact this is exactly what it was intended to do we can exploit warping to provide pseudo 3d and just before we get stuck in there's no reason why you couldn't use a perspective transform instead of an orthogonal transform if you happen to like games made of cubes but let's start by looking at the difference between a perspective projection and an orthographic projection here I'm looking at a scene from a top-down perspective and I've got the players eye here looking into the scene there are four cubes in this scene under a traditional perspective projection we see things as they get further away get smaller this is how we perceive the world around us pretty much everything disappears to what's known as a vanishing point is somewhere on the horizon and this happens because we take a points coordinate in 3d space relative to that vanishing point and we scale its X&Y position on the screen by 1 over how far away it is from us so our screens x-coordinate will become the object's x-coordinate divided by the objects instead coordinate and the same applies to Y as the Z coordinate gets larger by being further away from the camera then the result of this calculation gets smaller because it is inversely proportional to Z as these offsets get smaller relative to that vanishing point they get closer to it and if we maintain this relationship consistently we get this three-dimensional effect and we can scale everything this way so if one of these cubes happen to move let's save this one and it was moving across the screen and so was this one at exactly the same velocity this scaling would give us the effect that the cube closer to us is moving faster but that's required to maintain this illusion of perspective even though in reality they're moving at the same speed incidentally this is my objects which are really far away never appeared to move like stars for example in the night sky this approach is simplified for this explanation and I have gone into a thoroughly detailed derivation of this in my three-dimensional graphics series however the principle is that x and y are related to Z bear an inverse proportional relationship as ded gets bigger x and y gets smaller as Zed gets smaller x and y get bigger an orthographic projection even though it sounds a lot more complicated is in fact much much simpler in the simplest of terms the orthographic perspective transformation simply ignores the Z value so whereas before we had a particular coordinate that existed as X Y Z in world space that coordinate still exists there but when we project it we just ignore the Z value so now when we're looking into the scene we don't get any perspective at all there is no vanishing point and this has the effect of making parallel lines remain parallel on the scene here we're quite familiar with the concept of field of view well now it's almost as if the field of view is completely flat and we're looking at it from all locations at once rather than a single point so what we see on the screen is in fact a direct straight ray normal to the screen this style of projection means we simply cannot see the cubes that are behind the cubes at the front however we can still apply all of the traditional world transformations that were familiar with so let's say we position the camera slightly too right here and we've also moved it up a little bit and we're looking down on the scene from an angle parallel lines remain parallel so now we will see our four cubes and even though they look three-dimensional they don't follow these rules of perspective in principle this cube is the farthest away but it is exactly the same size as the cube that is closest to the camera and the cubes edges have remained in parallel in fact our entire worlds axes have remained orthogonal to each other and I find this approach is a great way of retaining a two-dimensional game aesthetic even though your game is stored and manipulated as a 3d World it isn't applicable to all scenarios some things just simply will not look right but used in the right circumstances I quite like it working in an orthographic space like this also provides some optimization advantages and we'll look at those later in the video I mentioned at the start that what I really want to develop here is a platform that I can reuse for other projects later in the year I think it would get a bit frustrating to have to recode a complete game base each time I wanted to describe a new algorithm so we'll probably see this little editor and world render pop up quite a bit in the future as such I want to talk about the very basic data structures that I'm going to use and I want them to be very basic because I have no idea how I'm going to change them in the future and I want them to be quite flexible the world is simply a 2d array of cells and it has a width and a height and in the top left we have 0 0 and somewhere in the world we have a cell this 2d array I'm going to wrap in a class called world and the cell I'm going to wrap in a class called cell imaginative huh at this basic level there's a few things that needs to describe what a cell is firstly is it a wall ie do we actually render it as a queue or do we just render it as some flaw the cube is I hope we all understand consists of 6 faces and for each one of those faces I want some sort of ID specifies what gets drawn for that face and I'm adopting the convention of this is the South Face North Face east face west face top and floor and the reason it's not bottom is because I never anticipate actually viewing the cube from underneath it's always going to be viewed from above so where is these five faces all have this if it's normals pointing outwards the floor has it pointing inwards I've gone ahead and created a skeleton pixel game engine application it's 640 by 480 pixels and each pixel is 2 by 2 screen pixels and because there isn't much which is particularly novel or special in this code for this video I'm going to go through it quite quickly I'm going to add a little struct which represents our cell with the wall flag for its existence and I'm going to use a 2d integer vector type for the ID the ID represents what's going to be drawn on the surface of the cube for that particular face and I've constructed a single file texture that contains all of the different phases for the cubes that I'm going to have in this game at the top here I've got the various floors and then I've got the various wall types I've also thrown in some generic colors they may come in handy too now I've had this particular sprite pack for about 20 years I don't know who the original author was and as such I'm uncomfortable about providing it as part of the source code so if you do end up using this code you may need to hack together a small sprite of your own each of the tiles in this fight are 32 by 32 pixels and what I'm interested in is the starting coordinate of a particular tile because that coordinate is all the information I need to tell me which face should I draw and that's why I'm using a coordinate type to store that identifier next I'm going to create a small class called world I'll give it a constructor and the world is fundamentally going to be a two-dimensional array I'm deciding to mix things up a bit so I'm going to use a vector for that rather than allocating it explicitly I'll probably want to get the world's size quite often so I'm going to make that a public member very and I'm going to add a create function which initializes the vector which defines the size and it also resizes the vector of the cells I'm then going to add a get cell function now normally when I do something like this I would return a pointer to the specific cell however I want to do things a little bit differently so I'm going to return a reference instead because and I'm sure many of you will point out you should never really to return a pointer to a vector element this does present a small problem then one of the things I want to check for in my get cell function is that I'm actually addressing a coordinate that exists within the size of the world and I'll do that translation as we have done in every single video I'm sure you can all sing along now Y x width plus X however what do I return if the cell lies outside of the world normally I would return a null pointer I can't do that if I'm returning a reference I've got to return something so I'm going to create a special one-off cell called a null cell and that's just something to satisfy the reference being passed back to whatever is called the get cell function we don't care about the contents of the null cell it's a null cell all the walls could be zero there could be whatever we want to not draw the cell we can define that later but this ensures that we're never operating on invalid memory in our 2d array storing the world it is a bit doing things differently for the sake of doing things differently so added to the main class of our game I'm going to add variable world of type world and in on user create I'm going to construct a default world of 64 by 64 cells I don't want to have to hand draw all of those every single time so I'm going to iterate through the size of the world touching every single cell and I'm going to basically make the entire world a floor so I'm passing into the get cell function a vector 2d type because this function returns the reference I can access the properties directly I'm then going to set up the six IEDs required to try and make the engine as flexible as possible I'm taking out all of the magic numbers so here I've got a vector tile size I'll need to add that to my game and my tiles are 32 by 32 pixels I've also got six possible faces and just so we don't get confused when we're looking at the numbers I'm going to name those faces in an enum when you multiply two OLC vector types it does element-wise multiplication this is a very useful thing in 2d engines this means we could use tiles that aren't 32 by 32 they could be rectangular tiles and this would still work what I'm passing in here is the coordinate of the tile in my texture so for the floor texture I'm passing in the third one along on the top row so if we go and look at the texture so zero one two three it's this sort of noisy gray and my default walls are zero along and six down so zero one two three four five six it's this wall down here pretty much everything in this demo application is going to be rendered as a decal and decals always need a sprite as a source and whilst I've been experimenting with decals one of the things I found myself doing is always creating a small little wrapper class called a renderable and I find if I'm frequently always doing this then it'll probably make its way into the pixel game engine main core a renderable is just a convenient way of simultaneously loading a sprite and a decal you pass in the file path to the image you're interested in it creates the new sprite and it associates that sprite with a new decal we can also put a destructor here just to make sure it's clean and tidy now we need to add the assets to the game so I'm going to create a variable rend or walls of type renderable any long use a crate I'm going to load the texture that I was showing before all of the drawn side of the cubes that you can see aren't quadrilaterals they're polygons with four sides this makes sense because our tiles have four sides and the pixel game engine understands the decals have four sides too and it's the positioning of the four points of that quadrilateral which determine its warpage so at some point I'm going to create a container of quads which need to be drawn to the screen so I'll create a small structure in preparation for that the world we're working in is actually a 3d world so I need a quick and dirty 3d vector type a quad is defined by these four points in world space XY and Z and I'm also going to include a 2d vector type which mirrors the texture coordinate that were interested in for drawing that quad before we can start drawing things though we need to store some information about our camera the camera is always going to look at a particular point on the 2d plane that represents the floor of our world and we have an angle around the y axis which is normal to that floor plane that allows us to rotate the world I'm also going to store the cameras pitch which is the rotation in the x axis relative to the screen finally I'm going to store the cameras zoom which is how close do we get to the objects and we'll see that in orthographic space zoom is implemented a little bit differently to how you might expect every tile in the game world can be represented as a cube so I'm going to add a function called create cube that will generate the vertices in the right place for us create cube returns 8 3d vectors in the form of an array and it takes in the 2d cell coordinate which tile is it in our world the angle of rotation of the world the angle of pitch F scale is going to be our zoom and the 3d location of where the camera is in the world now because I like to keep things understandable on this channel I've separated out the following mathematical procedures and each stage is going to result in a cube with a slightly different transformation we'll start by defining the unit cube so this is a cube with no transformation sat at the origin and for those with a keen eye you may have noticed something rather strange why is I unit cube using the F scale value rather than 1 well it's just a small optimization I've no need to multiply those ones by F scale but I'm also going to be applying all of these transformations in screen space and that's a bit different to a standard way of thinking when it comes to 3d applications well we would normally take this world coordinate space and translate it into screen space working with orthographic perspectives means I can work directly in screen space and this can be a little bit more into to think about our unit cube is created at the origin but the first thing we'll need to do is translate it to its position within the world and I'll do this but also take into account the cameras location in the world so this is the tile coordinate and it's multiplied by our scaling value and I offset it by the camera you'll see that I've translated the 2d x and y-coordinate of the cell within the world into the X and z coordinate within the 3d world the unit cube still exists within the pixel space of the screen now I want to apply the world rotation and even though I said we were going to avoid matrices fundamentally this is a little matrix calculation for rotation in the y axis around the origin so our cube has been offset to its world location and now that is rotated around the origin the odd thing to think about here is I'm still doing all of this in screen space which means I don't need to create a view matrix or camera matrix or an equivalent of once we've rotated the whole world around to the y axis I then want to change the pitch so this is the angle we're looking at the world from the side kind of and this is again just a little 3d matrix rotation around the x axis the x axis is relative to going across the screen horizontally and before the y axis was relative to going up and down the screen vertically now it's time to do our orthographic projection and you'll see it's actually incredibly trivial all we're doing really is offsetting our world cubes coordinate by half the screen so this ensures that the camera's focal point is in the middle of the screen and not the top left what you'll notice about this particular projection is that there is no influence from the Z coordinate we still pass the Zed coordinate through because it's useful but we don't manipulate our x and y values based on that dead in the source file that's accompanying this video and you can get that from the github in the link below I've also included this code which is a generic form of the orthographic transformation I can optimize it to be this simple because I'm assuming we're looking at the entire screen and so far all of our transformations have been in screen space however if you wanted to project to a viewport say a smaller sub rectangle on the screen where you might have some additional inventory information or other things you don't want to render to the whole screen this is the generic form to do that where you can specify explicitly the left right top bottom near and far boundaries of your world even so you'll see that there is no influence of the Zed axis on the x and y axes so that's included in the download but I'm going to remove it for now the last thing to do is return our projected cube so let's just have a little recap of what's going on we're passing in the particular cell coordinate in the world of a cube and the camera parameters we create a unit cube we then translate it to its position in the world we rotate that world around the y axis we rotate the world around the x axis to give us some tilt and then we project the cube which in this case is just offsetting it so that the look at point of the camera is in the middle of the screen and not the top left and that's that we result with an array of transformed x y&z coordinates for our cube we now know where they are in screen space now it's time to think about drawing some stuff and we'll do this in on user update I mentioned before that effectively we're going to need a container filled with quadrilaterals the things were going to draw so I created a vector of my S quad type and I'm making no attempt at optimizing here I'm going to draw the entire world all the time even if I know I can't see most of it so I'll create two nested for loops that iterate through every single cell in the world and for each cell we're going to get the quads that we want to draw and those quads represent the faces of the cubes and I'm going to tidy this up by creating a function called get face quads which takes in the cells x and y coordinate also passes in the camera information which is important and it's going to append to that vector any quads that represent that particular cube I'll have the body for this function here and what's important is that we're passing in this vector by reference at the end in order to know which faces we need to construct we're going to have to create the cube so here I'm calling the create cube function we just created just to keep the typing down I'm going to create a small reference to the cell at the location and this is important because all create cube does is construct some geometry for us it doesn't really know anything about texturing information conceptually this is how the unit cube is constructed and I've identified my vertices in the following order 0 1 2 3 4 5 6 7 now I'm drawing this out there's probably better ways to do it but it really doesn't matter what it tells me is that if I want to define the southern face of my cube which is this one that I need to use points 0 1 2 & 3 I want the sprite to appear kind of correct so if we have the door sprite for example then I want my quadrilateral when I draw it as a decal to kind of reflect the correct thing now decals are drawn in a slightly different order you specify the points to a decal like a u-shape so if I wanted to define the southern face then the quadrilateral needs to be formed of the points 3 0 1 and 2 and this ordering is important because it will change the direction the sprite faces and we have to think about it relative to the rest of the cube so if I wanted to draw the north and face then it's important that I remember from this angle that it specified the other way round so for north I want to draw 6 5 4 and 7 it's really just about maintaining consistency across the faces therefore I'm going to create a little lambda function to help me define the faces of the cube where I can pass in my ID of which vertex I'm looking at and which face those vertices form might returned projected cube array contains those vertices in screen space so I'm just going to index into that array which ones I need to make up that face the final element is the texture coordinate that were choosing to draw for that face here we have decoupled the quads that were drawing from the tile in the world if the cell is not considered a wall and all we need to provide is a quad that reflects the floor so I'll call my make face lambda with the indices that represent the floor and tell it to extract the floors texture coordinate' for that particular cell if we're not drawing floor then we're drawing wall and walls consists of five visible faces we have the south wall the northern wall the eastern wall the western wall and the top we can't see the floor we may change this in the future if our walls contain two transparency information we might actually want to render the floor but for now I'm assuming that the walls are solid each time we call the make face lambda function we add that quad to our vector quads which means our get face quads function returns all of the information necessary to draw that particular tile cube to the screen in fact I don't need this to do anything else so now let's draw these quads first thing I'm going to do is clear the screen to black and then I'm going to iterate through the vector of quads I want to draw and I'm going to call the draw partial warped decal function which is a pixel game engine function which will take in our screen space quad coordinates and the texture coordinates that we need and draw it with the appropriate warped distortion on the screen it's going to get its image source from the render walls renderable we created earlier and the screen is two-dimensional but my quadrilateral contains three dimensional coordinates information we've already done the orthographic perspective transform so we know that the Zed information is now irrelevant so here I pass in the for 2d vector coordinates just comprising of the X&Y components of the 3d vectors then I pass in the source texture coordinate and finally I need to pass in the texture size in principle those last two parameters specify the coordinates of the tile that were interested in top left and gives me the width in the height of the full tile so it's going to extract this one tile and warp it between the four screen space coordinates that we've supplied I think at this point it's good to take a look like to make sure things are working and well what we can see is that the bottom right is certainly textured with some sort of tiled texture and it is our floor texture that we've specified but I have no means of interacting with this application so at the start of our on user update function I'm going to handle some user input the W and s keys are going to adjust the tilt of the camera up and down the D and a keys are going to handle the rotation of the camera I'm going to use Q and Zed keys to handle the zoom so all this is going to do is change the scale of our unit cube originally because don't forget all of these transformations are in screen space and that's the distinct difference between doing genuine 3d world projections and what we're doing here so now that we've got control of those let's try it again so if I press the a and D Keys I can rotate my world around at the moment were centered on the very top left zero zero of the world we'll need to add in a cursor but we'll do that in a minute the W and s keys are just the pitch and I think you can just start to see that happen q and Zed allow us to zoom in and out and here we can see the texture is getting larger in this cases we're zooming in and if I keep this held down we'll zoom all the way out to see the whole world I can rotate the whole world around and perhaps now we can see pitch so we are starting to get this 3d effect and we're also starting to get this sort of wonkiness that is associated with orthographic perspectives it looks wonky because we're so tuned to things looking in perspective in 3d applications what we can see is that the edges of our entire world remain parallel and they shouldn't in a normal perspective situation you'll often find that mechanical design CAD software the designers prefer to work in this perspective so they can see that things are the same length regardless of how deep they are on the screen I want to add in some convenience functions to snap the rotation to give an angle so we can always line up with the direct axes of the world let's take a look so if I press the different keys on the numpad we can look at the world directly along a specific axis or an XY axis or x ed in this case to move around the world we're going to need a cursor and the cursor is going to represent a particular cell in our world since the world can be a lot larger than what we can see it's quite useful to also adjust the camera position depending on the location of this cursor so I'm going to do exactly that and I'm going to make the arrow keys responsible for positioning the cursor now I could do clever things and have it responsible for positioning the cursor relative to the direction and the angle you were looking at the scene but for now I'm keeping it really simple so if you're looking at the scene from the left hand side the cursor keys are not going to be translated accordingly but here I'm just responding to the key being pressed adjusting the cursors value accordingly and clamping it to the size of our world so we can't position the cursor outside of the world and since we have a cursor now we have the ability to select a specific cell so if the spacebar is pressed I'm going to toggle the wall flag of our cell let's take a look now well I can move the screen around but I can't see a physical cursor but I'll place a cell and there we go we can see that the cube is now generated I can't replace several cells say well I'll just zoom in a little bit and with the W and D keys we can start to rotate the world but we can see there is now a problem the order in which we want to draw things is quite important or else our perspective is completely confused but hey one problem at the time let's first of all draw a cursor to draw the cursor I'm going to do exactly the same process I'm just going to draw an additional tile on top of the cube that already exists in fact I'm going to use the same cube geometry I'm just going to change the nature of the decal so here is the selection decal that I want to use it's a yellow boundary that fades to alpha in the middle since this is another resource I'm going to add an additional renderable which is our selection cursor after we've drawn all of the visible quads I'm then going to draw the cursor and the process is pretty much the same I'm going to clear this temporary vector of quads now and I'm going to get the face quads for the item that is at the location of the cursor and in exactly the same way as above I'm going to draw the quads that represent that tile but I'm using my selection decal instead so let's take a look so now we can see the cursor quite nicely also zoom in and it follows it around and if I place the cube the cursor itself becomes a cube because it's just drawing the same faces just with a different decal so now we can navigate the world a little bit more sensibly but let's now address the elephant in the room it looks rubbish what we see here are things being drawn in the incorrect order so we're typically scanning from the top left to the bottom right and we look at which faces are visible for a given cube but then on the next cube down we're drawing over what we've already put there we need to do some sort of sorting and rather ironically even though we've completely rejected the Zed components of all of the vertices that we're drawing in order to see the textures we're going to use the Z component to sort things in the correct order because the Z component in world space which don't forget right now our world space is also our screen space is still valid it's still further away from the camera and this is actually really trivial to do because all hail the standard library for providing was with a sort function the vector of quads that we've got even though we're only using the x and y components when we're drawing things still have the Zed component and that Zed component is still accurate so I'll call the sort function passing in a custom lambda function that determines how we compute average said value for a given quad in fact that's exactly what I'm doing I add up all of the Z components for each face and divide it by four and then I'll compare those and return true or false accordingly so the sort algorithm can do its work let's take a look now here I've drawn a few cubes and as I rotate we now see that things are drawn in the correct order relative to where the camera is the nice thing is our selection cube we chose to draw later so you can still see it if it's behind an object as I move the selection cursor around it yields an interesting observation for this particular cursor we see there are five faces being drawn yet of course in a 3d world if we're looking at a cube we can only ever see three faces maximum at a given time this means we're drawing twice as much than we need to therefore it's worth rejecting faces that we can't see and here is where orthographic projection can come and help us traditionally to work out whether or not a face is visible we need to extract the normal to the face and if that normal vector has a Zed component which is away from the camera then clearly we cannot see the face so we only want to accept faces that have normals with positive Z components under a perspective projection we would have to do this for all cubes because the very nature of perspective means we can see different sides of the cube depending on where we're looking at however in our simple application here all of our cubes are identical and this is a very important thing it doesn't matter how far away they are from the camera or where they are in the world as we discussed before everything remains parallel and axis aligned so regardless of rotation and orientation I will always see the same three faces of our axis aligned cubes so I only need to check for six surface normals in our entire scene to work out which surfaces are visible this is a great optimization that we can do because it's an orthographic transform so let's add in a function which determines the visible faces for any cube in our system I've added an array here SiC boolean's and I'm going to add in an additional function calculate visible faces and this will take in a particular cube to this function I'm going to add a little lambda function called check normal which is going to look at three vertices of our cubes face and perform the two-dimensional cross-product now that might not make much sense to the mathematicians out there we all are very familiar with three-dimensional cross products it's the returns of vector which is orthogonal to the two input vectors we can do something similar in 2d by assuming that one of those axes is a plane and therefore will provide a scalar result which is the magnitude of this sort of virtual vector coming out of that plane and so we can use the sine of this magnitude to tell us whether or not that particular quadrilateral surface is wound away from us or wound towards us and I can do this for all six faces of our cube I'm just choosing the indices we've already specified for the faces and in fact you'll see they're quite similar so for the floor its 401 there and down here when we were making the flies just for once just the first three indices we just need three points on that particular face so if we call this function with a cube it will set our six boolean variables to tell us whether or not that face is visible and therefore when we're creating our faces and adding them into the vector of quads to be rendered we should check against this array of boolean to see if the corresponding face is in fact visible this way we'll only draw what we can see and we'll need to do this for all faces so now when we're generating the quads to be drawn if any of them have a surface normal that points away from the camera we're not even going to add them to the list of things to be drawn now you might think that's really expensive we can't do that for every single tile or cube that we can see in the game and because we're using an orthographic perspective transformation we don't have to we only have to do it for a single cube because all of our cubes are identical regardless of where they are on the screen and so I'm going to use our create cube function again I can pass in any cube coordinate it doesn't matter but what does matter is the angle of the camera and the zoom and things like that and based on the geometry returned from that function I'm going to call our calculate visible faces function which will set those billions so now when I run the application can I'll place a few walls even though it looks the same as before we're only rendering the things we can see and we can prove this by looking at the cursor selection cube it now only consists of the three visible faces whereas before it consisted of the faces we can't see we've got one thing left to add now and that is a way to select the textures from our source texture to paint on the walls we need an edit mode and this is so simple and trivial you might actually smile I'm going to add in a 2d vector tile cursor which represents the selected tile within our texture an in on user update I'm going to be responsive to the tab key being held down so you hold down the tab to bring up edit mode and if we're in edit mode we don't want to do anything else we're not drawing the rest of the scene so I'll just return early you're going to select which tile you want with the mouse so for convenience I'm going to get the mouse's coordinates and store them in a vector from the top left of the screen I'm going to draw the sprite this is the sprite that contains all of the tiles I need to know which sprite I've got selected so I'm going to draw a rectangle at the right location start tile cursor multiplied by the tile size and if the mouse is clicked then I'm going to specify the tile cursor position to be the mouse's coordinates integer divided by the tile size and that's it that's edit mode so we'll click play and if I hold down the tab key we see our sprite appear and I can click on an individual tile and we can see it gets a nice border around it so we know which one it is all that remains is a mechanism to assign which particular tile is selected to the given face of the cell at our cursors location and I'm going to do that rather than elegantly by assigning the faces south north east west top and bottom to numeric keys so if you press the 1 key we're going to affect the northern face of the cell we just get the cell at the current cursor location we get the face and we set the ID which remember is a texture coordinate to wherever our tile cursor is in the texture space multiplied by the tile size so here I've got a cube in the middle of the screen and I want to paint this particular texture like I'm fairly menacing Knight onto this face and so I know that the one key represented the northern face and this is the northern face because up here is our top left that's our 0 0 so technically we can't see the southern face at the moment and if I rotate the camera we can see we've only applied it to that face if I now press the 2 key we can apply it to that face the 3 key we'll apply it to that one the 4 key will apply to that one 5 applies it to the floor and 6 applied it to the top okay one little final bonus thing to add and that is as we're moving the camera around it can be a bit jarring to just move in these fixed intervals there's a really quick little hack you can do to make smooth movement in games instead of specifying an angle directly we'll give it a target instead and I'll just create that variable up here once we've got this target we can now slowly adapt our actual camera angle towards the target by looking at the difference multiplying it by some number and that will be the speed of that adaptation and F elapsed time so let's take a look now so I live in some buildings and now as I move the camera around it's a far more pleasing transition and so there you have it I know it's been a bit quick this video but I wanted to establish this simple little platform because we'll be coming back to it in quite a few future videos we've looked at how we can exploit orthographic transforms to buy us some simplicity in rendering what I think is a rather quaint 2-dimensional aesthetic but using fundamentally a 3d data source because the world is easier to represent in 3d in this instance anyway if you've enjoyed this video please give me a big thumbs up have a think about subscribing the source code is available on the Hubb come and have a chat on the discord server and i'll see you next time take care
Info
Channel: javidx9
Views: 67,341
Rating: undefined out of 5
Keywords: one lone coder, onelonecoder, learning, programming, tutorial, c++, beginner, olcconsolegameengine, command prompt, ascii, game, game engine, pixelgameengine, olc::pixelgameengine, Orthographic Projection, Pseudo 3D
Id: Ql5VZGkL23o
Channel Id: undefined
Length: 42min 14sec (2534 seconds)
Published: Sat Apr 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.