public static Color Divide(this Color c, float n) { return(new Color( (byte)(c.R / n), (byte)(c.G / n), (byte)(c.B / n))); }
private void LoadTextures() { Texture allWalls = new Texture(ContentPath + "walls.jpeg"); int Columns = (int)allWalls.Width / TextureSize; int Rows = (int)allWalls.Height / TextureSize; // cookie undid his stupid! :D WallTextures = new Texture[Columns * Rows]; for (int cellY = 0; cellY < Rows; cellY++) { for (int cellX = 0; cellX < Columns; cellX++) { WallTextures[(Columns * cellY) + cellX] = new Texture((ushort)TextureSize, (ushort)TextureSize); for (int texX = 0; texX < TextureSize; texX++) { for (int texY = 0; texY < TextureSize; texY++) { Color wallPixel = allWalls.GetPixel(cellX * TextureSize + texX, cellY * TextureSize + texY); // TODO: Workaround for chroma edge case, remove this line when fixed wallPixel = new Color(wallPixel.A, wallPixel.B, wallPixel.G, wallPixel.R); WallTextures[(Columns * cellY) + cellX].SetPixel( texX, texY, wallPixel ); } } WallTextures[(Columns * cellY) + cellX].Flush(); } } }
public static void Rectangle( this RenderContext context, ShapeMode mode, Vector2 position, Size size, Color color ) => context.Rectangle(mode, position, size.Width, size.Height, color);
public static void Circle( this RenderContext context, ShapeMode mode, Vector2 position, float radius, Color color ) => context.Circle(mode, position.X, position.Y, radius, color);
public Label(Vector2 position, string text, TrueTypeFont font = null, int fontSize = 12) : base(position, Vector2.One) { Text = text; Color = Color.White; Font = font ?? new TrueTypeFont(UiContentLoader.Instance.DefaultFontPath, fontSize); Font.Size = fontSize; }
public static void Rectangle( this RenderContext context, ShapeMode mode, Vector2 position, float width, float height, Color color ) => context.Rectangle(mode, position.X, position.Y, width, height, color);
public static void Triangle( this RenderContext context, ShapeMode mode, Vector2 a, Vector2 b, Vector2 c, Color color ) => context.Triangle(mode, a.X, a.Y, b.X, b.Y, c.X, c.Y, color);
public static void Ellipse( this RenderContext context, ShapeMode mode, Vector2 position, Vector2 radii, float rotation, Color color ) => context.Ellipse(mode, position.X, position.Y, radii.X, radii.Y, rotation, color);
public static void DrawString( this RenderContext context, IFontProvider font, string text, float x, float y, Color color ) => context.DrawString(font, text, x, y, (_, _, p) => new(p) { Color = color });
public static void Arc( this RenderContext context, ShapeMode mode, Vector2 position, float radius, float startAngle, float endAngle, Color color ) => context.Arc(mode, position.X, position.Y, radius, startAngle, endAngle, color);
/// <summary> /// Draws a rectangle used for HUD elements. /// </summary> /// <param name="context">RenderContext to draw onto</param> /// <param name="rect">The rectangle to draw</param> /// <param name="lineWidth">How thicc the line should be</param> /// <param name="whiteOrCopyPen">0 = normal colors, 1 = white and right half isn't drawn, 2 = both halves are dark</param> private void DrawHudRectangle(RenderContext context, Rectangle rect, int lineWidth, int whiteOrCopyPen) { float oldThickness = Graphics.LineThickness; Graphics.LineThickness = 1; rect.X++; rect.Width--; // Top and left lines Color colorToDraw = whiteOrCopyPen == 1 ? Constants.RightRectangleColor : Constants.LeftRectangleColor; for (int i = 0; i < lineWidth; i++) { context.Line(new Vector2(rect.X, rect.Y + i), new Vector2(rect.X + rect.Width - i - 1, rect.Y + i), colorToDraw); context.Line(new Vector2(rect.X + i, rect.Y), new Vector2(rect.X + i, rect.Y + rect.Height - i - 1), colorToDraw); } if (whiteOrCopyPen != 1) { // Right and bottom lines Color newColorToDraw = whiteOrCopyPen < 2 ? Constants.RightRectangleColor : Constants.LeftRectangleColor; for (int i = 0; i < lineWidth; i++) { context.Line(new Vector2(rect.X + rect.Width - i, rect.Y + i + 1), new Vector2(rect.X + rect.Width - i, rect.Y + rect.Height), newColorToDraw); context.Line(new Vector2(rect.X + rect.Width, rect.Y + rect.Height - i - 1), new Vector2(rect.X + i, rect.Y + rect.Height - i), newColorToDraw); } } Graphics.LineThickness = oldThickness; }
public static void Rectangle( this RenderContext context, ShapeMode mode, RectangleF rectangle, Color color ) => context.Rectangle(mode, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, color);
public static void Line( this RenderContext context, Vector2 start, Vector2 end, Color color ) => context.Line(start.X, start.Y, end.X, end.Y, color);
public static void Clear( this RenderContext context, Color color ) => context.Clear(color.R, color.G, color.B, color.A);
protected override void Draw(RenderContext context) { context.Clear(Color.Gray); int leftRayX = 0, leftRayY = 0, rightRayX = 0, rightRayY = 0; List <Object> spritesDrawn = new List <Object>(); for (int x = 0; x <= ScreenWidth; x += ScreenWidthDivision) { // Calculate the direction needed for the ray, and the cam space double cameraX = 2 * x / (double)ScreenWidth - 1; double rayDirX = DirX + PlaneX * cameraX; double rayDirY = DirY + PlaneY * cameraX; // Where is the ray located in the map? int mapX = (int)PlayerPos.X; int mapY = (int)PlayerPos.Y; // These variables tell us how far until the next X side of the matrix // Or the next Y side, relative to the current position double sideDistX; double sideDistY; // Same principle as the sideDistX/Y except its from the *last* X or Y side // Also prevent deviding by 0 (does C# even care about this? Should look that up) double deltaDistX = (rayDirY == 0) ? 0 : ((rayDirX == 0) ? 1 : Abs(1 / rayDirX)); double deltaDistY = (rayDirX == 0) ? 0 : ((rayDirY == 0) ? 1 : Abs(1 / rayDirY)); double perpWallDist; // Either -1 or 1 depending on what direction we need to step in next int stepX; int stepY; int hit = 0; // Did we hit a wall? int side = 0; // Which side we hit the wall on (North/South or East/West) // Actually calculate side and directions if (rayDirX < 0) { stepX = -1; sideDistX = (PlayerPos.X - mapX) * deltaDistX; } else { stepX = 1; sideDistX = (mapX + 1.0 - PlayerPos.X) * deltaDistX; } if (rayDirY < 0) { stepY = -1; sideDistY = (PlayerPos.Y - mapY) * deltaDistY; } else { stepY = 1; sideDistY = (mapY + 1.0 - PlayerPos.Y) * deltaDistY; } // Finally, Actually, do the raycasting using the DDA algorithm while (hit == 0) { // Jump to the next map square/X direction/Y direction if (sideDistX < sideDistY) { sideDistX += deltaDistX; mapX += stepX; side = 0; } else { sideDistY += deltaDistY; mapY += stepY; side = 1; } if (Abs(mapX) > 255 || Abs(mapY) > 255) { break; } if (mapX >= WorldMap.GetLength(0) || mapY >= WorldMap.GetLength(1)) { continue; } if (mapX < 0 || mapY < 0) { continue; } // Check if ray has hit a wall if (WorldMap[mapX, mapY] > 0) { hit = 1; } if (RenderSprites) { // Check if a sprite is in view if (SpriteMap[mapX, mapY] != null) { if (!spritesDrawn.Contains(SpriteMap[mapX, mapY])) { // Calculate sprite distance from the player so we can render it properly Object sprite = SpriteMap[mapX, mapY]; double spriteDist; // Calculate distance projected on camera direction (Euclidean distance will give fisheye effect!) if (side == 0) { spriteDist = (mapX - PlayerPos.X + (1 - stepX) / 2) / rayDirX; } else { spriteDist = (mapY - PlayerPos.Y + (1 - stepY) / 2) / rayDirY; } int usableDist = (int)(spriteDist * 1000); sprite.ZIndex = usableDist; spritesDrawn.Add(sprite); context.Batch(() => sprite.Draw( context, PlayerPos, new Vector2((float)DirX, (float)DirY), new Vector2((float)PlaneX, (float)PlaneY), new Vector2(ScreenWidth, ScreenHeight) ), (int)sprite.ZIndex); } } } } if (hit == 0) { continue; } if (x == 0) { leftRayX = mapX; leftRayY = mapY; } else if (x == ScreenWidth) { rightRayX = mapX; rightRayY = mapY; } // Calculate distance projected on camera direction (Euclidean distance will give fisheye effect!) if (side == 0) { perpWallDist = (mapX - PlayerPos.X + (1 - stepX) / 2) / rayDirX; } else { perpWallDist = (mapY - PlayerPos.Y + (1 - stepY) / 2) / rayDirY; } // Calculate height of the strip that we draw to the screen int lineHeight = (int)(ScreenHeight / perpWallDist); // Calculate lowest and highest pixel to fill in current stripe int drawStart = -lineHeight / 2 + ScreenHeight / 2; if (drawStart < 0) { drawStart = 0; } int drawEnd = lineHeight / 2 + ScreenHeight / 2; if (drawEnd >= ScreenHeight) { drawEnd = ScreenHeight - 1; } // Calculate texture rendering int texNum = WorldMap[mapX, mapY] - 1; // Subtract 1 so we are 0 indexed // Calculate where the wall was hit double wallX; if (side == 0) { wallX = PlayerPos.Y + perpWallDist * rayDirY; } else { wallX = PlayerPos.X + perpWallDist * rayDirX; } wallX -= Floor(wallX); // Calculate the X coordinate of the txture int texX = (int)(wallX * TextureSize); if (side == 0 && rayDirX > 0) { texX = TextureSize - texX - 1; } if (side == 1 && rayDirY < 0) { texX = TextureSize - texX - 1; } Color color; if (!FlatRender) { // Draw textures using strips instead of pixels int texY = Min(drawStart, -(lineHeight - ScreenHeight) / 2); Texture stripTex = WallTextures[texNum]; Rectangle SourceRectangle = new Rectangle(texX, 0, ScreenWidthDivision, TextureSize); Vector2 Position = new Vector2(x, texY); Vector2 Scale = new Vector2(1, (float)lineHeight / TextureSize); Color StripColor = side == 1 ? Color.Gray : Color.White; int usableDist = (int)(perpWallDist * 1000); context.Batch(() => { if (!stripTex.ColorMask.Equals(StripColor)) { stripTex.ColorMask = StripColor; } context.DrawTexture(stripTex, Position, Scale, Vector2.Zero, 0, SourceRectangle); }, usableDist); } else { // Debugging wall colors for flat renderer color = (WorldMap[mapX, mapY]) switch { 1 => Color.Red, 2 => Color.Green, 3 => Color.Blue, 4 => Color.Beige, 5 => Color.Aquamarine, 6 => Color.HotPink, 7 => Color.Purple, 8 => Color.Brown, _ => Color.Yellow, }; // Darken the color to create perspective if (side == 1) { color.R /= 2; color.G /= 2; color.B /= 2; } int usableDist = (int)(perpWallDist * 1000); // Actually draw the pixels of the stripe as a vertical line DrawColorStrip(context, x, drawStart, drawEnd, usableDist, color); } } if (RenderSprites) { foreach (Enemy enemy in EnemyList) { // Calculate distance projected on camera direction (Euclidean distance will give fisheye effect!) float dX = PlayerPos.X - enemy.PosX; float dY = PlayerPos.Y - enemy.PosY; // Calculate sprite distance from the player so we can render it properly double spriteDist; spriteDist = Sqrt(dX * dX + dY * dY); int usableDist = (int)(spriteDist * 1000); enemy.ZIndex = usableDist; context.Batch(() => enemy.Draw( context, PlayerPos, new Vector2((float)DirX, (float)DirY), new Vector2((float)PlaneX, (float)PlaneY), new Vector2(ScreenWidth, ScreenHeight) ), (int)enemy.ZIndex); } } context.DrawBatch(DrawOrder.FrontToBack); if (MiniMap) { // Move these to meta variables later, im tired Vector2 MinimapSize = new Vector2(300, 300); Vector2 MinimapPosition = new Vector2(ScreenWidth - MinimapSize.X, 0); // Render minimap for (int x = 0; x < MapWidth; x++) { for (int y = 0; y < MapHeight; y++) { // Debugging wall colors for flat renderer var color = (WorldMap[y, x]) switch { 1 => Color.Red, 2 => Color.Green, 3 => Color.Blue, 4 => Color.Beige, 5 => Color.Aquamarine, 6 => Color.HotPink, 7 => Color.Purple, 8 => Color.Brown, _ => Color.Yellow, }; Vector2 tileSize = new Vector2(MinimapSize.X / MapWidth, MinimapSize.Y / MapHeight); Vector2 tilePos = new Vector2(tileSize.X * x, tileSize.Y * y); context.Rectangle(ShapeMode.Fill, MinimapPosition + tilePos, tileSize.X, tileSize.Y, color); } } Vector2 tileRefSize = new Vector2(MinimapSize.X / MapWidth, MinimapSize.Y / MapHeight); Vector2 playerDotSize = tileRefSize / 2; Vector2 playerDotPos = new Vector2(tileRefSize.X * PlayerPos.Y, tileRefSize.Y * PlayerPos.X); playerDotPos -= playerDotSize / 2; context.Rectangle(ShapeMode.Fill, MinimapPosition + playerDotPos, playerDotSize.X, playerDotSize.Y, Color.Red); context.Line(MinimapPosition + playerDotPos + (playerDotSize / 2), MinimapPosition + new Vector2(tileRefSize.X * leftRayY, tileRefSize.Y * leftRayX), Color.Red ); context.Line(MinimapPosition + playerDotPos + (playerDotSize / 2), MinimapPosition + new Vector2(tileRefSize.X * rightRayY, tileRefSize.Y * rightRayX), Color.Red ); foreach (Enemy enemy in EnemyList) { Vector2 enemyDotSize = tileRefSize / 2; Vector2 enemyDotPos = new Vector2(tileRefSize.X * enemy.PosY, tileRefSize.Y * enemy.PosX); enemyDotPos -= enemyDotSize / 2; context.Rectangle(ShapeMode.Fill, MinimapPosition + enemyDotPos, enemyDotSize.X, enemyDotSize.Y, Color.LimeGreen); } } DebugText = $"{PerformanceCounter.FPS} FPS\n" + DebugText; context.DrawString(DebugFont, DebugText, Vector2.Zero, (c, i, p, g) => new GlyphTransformData() { Color = Color.White }); }
private void DrawColorStrip(RenderContext context, int screenX, int drawStart, int drawEnd, int z, Color color) { if (drawEnd - drawStart <= 0) { return; } context.Batch(() => context.Line(new Vector2(screenX, drawStart), new Vector2(screenX, drawEnd), color), z); }
public static void Pixel( this RenderContext context, Vector2 position, Color color ) => context.Pixel(position.X, position.Y, color);