public Renderer(int screenWidth, int screenHeight) { ScreenWidth = screenWidth; ScreenHeight = screenHeight; WallSlices = new WallSlice[screenWidth]; WallSlicesY = new WallSlice[screenWidth]; }
private void DrawDirectionalSprite(SpriteBatch spriteBatch, Camera camera, Map map, GameObject sprite) { //translate sprite position to relative to camera double spriteX = sprite.Position.X - camera.Position.X; double spriteY = sprite.Position.Y - camera.Position.Y; int spriteMapX = (int)sprite.Position.X; int spriteMapY = (int)sprite.Position.Y; Texture2D tex = sprite.Texture; int texWidth = tex.Width; int texHeight = tex.Height; double objectOffset = 0; if (sprite.Orientation == Orientation.None) { string errorMessage = ""; errorMessage = String.Format("The orientation of the Sprite in the location [{0}],[{1}] has not been defined!", sprite.Position.X.ToString(), sprite.Position.Y.ToString()); throw new Exception(errorMessage); } Orientation spriteOrientation = sprite.Orientation; //Find the sprite offset, that is how deep is the sprite inside the block depending on the orientation X or Y if (spriteOrientation == Orientation.Horizontal) { if (camera.Plane.X > 0) { objectOffset = (sprite.Position.Y - (int)sprite.Position.Y); } else { objectOffset = 1 - (sprite.Position.Y - (int)sprite.Position.Y); } } else { if (camera.Plane.Y < 0) { objectOffset = (sprite.Position.X - (int)sprite.Position.X); } else { objectOffset = 1 - (sprite.Position.X - (int)sprite.Position.X); } } double transformY = 0; double transformStartY = 0; double transformEndY = 0; int drawStartX = 0; int drawEndX = 0; int drawCenterX = ComputeScreenX(camera, spriteX, spriteY, out transformY); // add or substract 0.5 because the position of the sprite is the middle if (spriteOrientation == Orientation.Horizontal) { drawStartX = ComputeScreenX(camera, spriteX - 0.5, spriteY, out transformStartY); drawEndX = ComputeScreenX(camera, spriteX + 0.5, spriteY, out transformEndY); } else { drawStartX = ComputeScreenX(camera, spriteX, spriteY - 0.5, out transformStartY); drawEndX = ComputeScreenX(camera, spriteX, spriteY + 0.5, out transformEndY); } //validate sprite's start and end if (drawStartX < 0) { drawStartX = 0; } if (drawStartX > ScreenWidth) { drawStartX = ScreenWidth; } if (drawEndX < 0) { drawEndX = 0; } if (drawEndX > ScreenWidth) { drawEndX = ScreenWidth; } //switch variables depending on the orientation of the camera if (drawStartX > drawEndX) { int tempX = drawStartX; drawStartX = drawEndX; drawEndX = tempX; } //temporal slices for the sprite WallSlice[] spriteSlices = new WallSlice[drawEndX - drawStartX]; //loop through every vertical stripe of the sprite on screen for (int x = drawStartX; x < drawEndX; x++) { //x-coordinate in camera space double cameraX = 2 * x / (double)ScreenWidth - 1; double rayPosX = camera.Position.X; double rayPosY = camera.Position.Y; double rayDirX = camera.Direction.X + camera.Plane.X * cameraX; double rayDirY = camera.Direction.Y + camera.Plane.Y * cameraX; //which box of the map we're in int mapX = (int)rayPosX; int mapY = (int)rayPosY; //length of ray from current position to next x or y-side double sideDistX = 0; double sideDistY = 0; //length of ray from one x or y-side to next x or y-side double deltaDistX = Math.Sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX)); double deltaDistY = Math.Sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY)); double perpWallDist = 0; //what direction to step in x or y-direction (either +1 or -1) int stepX = 0; int stepY = 0; int side = 0; //was a NS or a EW wall hit? if (spriteOrientation == Orientation.Horizontal) { side = 1; } else { side = 0; } //calculate step and initial sideDist if (rayDirX < 0) { stepX = -1; sideDistX = (rayPosX - mapX) * deltaDistX; } else { stepX = 1; sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX; } if (rayDirY < 0) { stepY = -1; sideDistY = (rayPosY - mapY) * deltaDistY; } else { stepY = 1; sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY; } mapX = spriteMapX; mapY = spriteMapY; //Calculate distance projected on camera direction (oblique distance will give fisheye effect!) if (side == 0) { perpWallDist = Math.Abs((mapX - rayPosX + (1 - stepX) / 2.0) / rayDirX) + Math.Abs(objectOffset / rayDirX); } else { perpWallDist = Math.Abs((mapY - rayPosY + (1 - stepY) / 2.0) / rayDirY) + Math.Abs(objectOffset / rayDirY); } //Calculate height of line to draw on screen int lineHeight = (int)Math.Abs(ScreenHeight / perpWallDist); //calculate lowest and highest pixel to fill in current stripe int drawStart = -lineHeight / 2 + ScreenHeight / 2; int drawEnd = lineHeight / 2 + ScreenHeight / 2; //calculate value of wallX //where exactly the wall was hit double wallX; if (side == 0) { //vertical wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2.0) / rayDirX + Math.Abs(objectOffset / rayDirX)) * rayDirY; } else { //horizontal wallX = rayPosX + (((mapY - rayPosY + (1 - stepY) / 2.0) / rayDirY) + Math.Abs(objectOffset / rayDirY)) * rayDirX; } wallX -= Math.Floor(wallX); double spriteCenter = 0; //Find the sprite center relative to the block containg it if (spriteOrientation == Orientation.Horizontal) { spriteCenter = (sprite.Position.X - (int)sprite.Position.X); } else { spriteCenter = (sprite.Position.Y - (int)sprite.Position.Y); } //Map correctly the texture depending on center of the sprite. //By default the center is an offset of 0.5 in X or Y axis if (spriteCenter > 0.5) { wallX = wallX - (spriteCenter - 0.5); } else if (spriteCenter < 0.5) { wallX = wallX - (spriteCenter + 0.5); } if (wallX < 0) { wallX = 1 + wallX; } //x coordinate on the texture int texX = (int)(wallX * (double)texWidth); spriteSlices[x - drawStartX].Depth = perpWallDist; spriteSlices[x - drawStartX].Height = lineHeight; spriteSlices[x - drawStartX].TextureX = texX; spriteSlices[x - drawStartX].Texture = tex; } //Draw the stripes for (int stripe = drawStartX; stripe < drawEndX; stripe++) { int drawStart = -spriteSlices[stripe - drawStartX].Height / 2 + ScreenHeight / 2; int drawEnd = spriteSlices[stripe - drawStartX].Height / 2 + ScreenHeight / 2; double spriteDepth = spriteSlices[stripe - drawStartX].Depth; //the conditions in the if are: //1) it's in front of camera plane so you don't see things behind you //2) it's on the screen (left) //3) it's on the screen (right) //4) ZBuffer, with perpendicular distance if (spriteDepth > objectOffset && stripe > 0 && stripe < ScreenWidth && spriteDepth < WallSlices[stripe].Depth && transformY > objectOffset) { spriteBatch.Draw( tex, new Rectangle(stripe, drawStart, 1, drawEnd - drawStart), new Rectangle(spriteSlices[stripe - drawStartX].TextureX, 0, 1, spriteSlices[stripe - drawStartX].Texture.Height), Color.White); } } }