488 lines
16 KiB
C#
488 lines
16 KiB
C#
/*******************************************************************************************
|
|
*
|
|
* raylib [textures] example - tiled drawing
|
|
*
|
|
* Example complexity rating: [★★★☆] 3/4
|
|
*
|
|
* Example originally created with raylib 3.0, last time updated with raylib 4.2
|
|
*
|
|
* Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
|
|
*
|
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
|
* BSD-like license that allows static linking with closed source software
|
|
*
|
|
* Copyright (c) 2020-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
|
|
*
|
|
********************************************************************************************/
|
|
|
|
namespace Examples.Textures;
|
|
|
|
public partial class DrawTiled : IExample
|
|
{
|
|
private const int OptWidth = 220;
|
|
private const int MarginSize = 8;
|
|
private const int ColorSize = 16;
|
|
|
|
public string Name => "Textures / Draw Tiled";
|
|
|
|
public string Title => "raylib [textures] example - tiled drawing";
|
|
|
|
public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow;
|
|
|
|
private int screenWidth;
|
|
private int screenHeight;
|
|
|
|
private Texture2D texPattern;
|
|
|
|
// Coordinates for all patterns inside the texture
|
|
private Rectangle[] recPattern;
|
|
|
|
// Setup colors
|
|
private Color[] colors;
|
|
private Rectangle[] colorRec;
|
|
|
|
private int activePattern;
|
|
private int activeCol;
|
|
private float scale;
|
|
private float rotation;
|
|
|
|
public void Init()
|
|
{
|
|
screenWidth = 800;
|
|
screenHeight = 450;
|
|
|
|
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
|
|
texPattern = LoadTexture("resources/patterns.png");
|
|
SetTextureFilter(texPattern, TextureFilter.Bilinear); // Makes the texture smoother when upscaled
|
|
|
|
// Coordinates for all patterns inside the texture
|
|
recPattern = new[] {
|
|
new Rectangle(3, 3, 66, 66),
|
|
new Rectangle(75, 3, 100, 100),
|
|
new Rectangle(3, 75, 66, 66),
|
|
new Rectangle(7, 156, 50, 50),
|
|
new Rectangle(85, 106, 90, 45),
|
|
new Rectangle(75, 154, 100, 60)
|
|
};
|
|
|
|
// Setup colors
|
|
colors = new[]
|
|
{
|
|
Color.Black,
|
|
Color.Maroon,
|
|
Color.Orange,
|
|
Color.Blue,
|
|
Color.Purple,
|
|
Color.Beige,
|
|
Color.Lime,
|
|
Color.Red,
|
|
Color.DarkGray,
|
|
Color.SkyBlue
|
|
};
|
|
colorRec = new Rectangle[colors.Length];
|
|
|
|
// Calculate rectangle for each color
|
|
for (int i = 0, x = 0, y = 0; i < colors.Length; i++)
|
|
{
|
|
colorRec[i].X = 2 + MarginSize + x;
|
|
colorRec[i].Y = 22 + 256 + MarginSize + y;
|
|
colorRec[i].Width = ColorSize * 2;
|
|
colorRec[i].Height = ColorSize;
|
|
|
|
if (i == (colors.Length / 2 - 1))
|
|
{
|
|
x = 0;
|
|
y += ColorSize + MarginSize;
|
|
}
|
|
else
|
|
{
|
|
x += (ColorSize * 2 + MarginSize);
|
|
}
|
|
}
|
|
|
|
activePattern = 0;
|
|
activeCol = 0;
|
|
scale = 1.0f;
|
|
rotation = 0.0f;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
// Update
|
|
//----------------------------------------------------------------------------------
|
|
screenWidth = GetScreenWidth();
|
|
screenHeight = GetScreenHeight();
|
|
|
|
// Handle mouse
|
|
if (IsMouseButtonPressed(MouseButton.Left))
|
|
{
|
|
var mouse = GetMousePosition();
|
|
|
|
// Check which pattern was clicked and set it as the active pattern
|
|
for (var i = 0; i < recPattern.Length; i++)
|
|
{
|
|
Rectangle rec = new(
|
|
2 + MarginSize + recPattern[i].X,
|
|
40 + MarginSize + recPattern[i].Y,
|
|
recPattern[i].Width,
|
|
recPattern[i].Height
|
|
);
|
|
if (CheckCollisionPointRec(mouse, rec))
|
|
{
|
|
activePattern = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check to see which color was clicked and set it as the active color
|
|
for (var i = 0; i < colors.Length; ++i)
|
|
{
|
|
if (CheckCollisionPointRec(mouse, colorRec[i]))
|
|
{
|
|
activeCol = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle keys: change scale
|
|
if (IsKeyPressed(KeyboardKey.Up))
|
|
{
|
|
scale += 0.25f;
|
|
}
|
|
if (IsKeyPressed(KeyboardKey.Down))
|
|
{
|
|
scale -= 0.25f;
|
|
}
|
|
if (scale > 10.0f)
|
|
{
|
|
scale = 10.0f;
|
|
}
|
|
else if (scale <= 0.0f)
|
|
{
|
|
scale = 0.25f;
|
|
}
|
|
|
|
// Handle keys: change rotation
|
|
if (IsKeyPressed(KeyboardKey.Left))
|
|
{
|
|
rotation -= 25.0f;
|
|
}
|
|
if (IsKeyPressed(KeyboardKey.Right))
|
|
{
|
|
rotation += 25.0f;
|
|
}
|
|
|
|
// Handle keys: reset
|
|
if (IsKeyPressed(KeyboardKey.Space))
|
|
{
|
|
rotation = 0.0f;
|
|
scale = 1.0f;
|
|
}
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Draw
|
|
//----------------------------------------------------------------------------------
|
|
BeginDrawing();
|
|
ClearBackground(Color.RayWhite);
|
|
|
|
// Draw the tiled area
|
|
var source = recPattern[activePattern];
|
|
Rectangle dest = new(
|
|
OptWidth + MarginSize,
|
|
MarginSize,
|
|
screenWidth - OptWidth - 2 * MarginSize,
|
|
screenHeight - 2 * MarginSize
|
|
);
|
|
DrawTextureTiled(texPattern, source, dest, Vector2.Zero, rotation, scale, colors[activeCol]);
|
|
|
|
// Draw options
|
|
var color = ColorAlpha(Color.LightGray, 0.5f);
|
|
DrawRectangle(MarginSize, MarginSize, OptWidth - MarginSize, screenHeight - 2 * MarginSize, color);
|
|
|
|
DrawText("Select Pattern", 2 + MarginSize, 30 + MarginSize, 10, Color.Black);
|
|
DrawTexture(texPattern, 2 + MarginSize, 40 + MarginSize, Color.Black);
|
|
DrawRectangle(
|
|
2 + MarginSize + (int)recPattern[activePattern].X,
|
|
40 + MarginSize + (int)recPattern[activePattern].Y,
|
|
(int)recPattern[activePattern].Width,
|
|
(int)recPattern[activePattern].Height,
|
|
ColorAlpha(Color.DarkBlue, 0.3f)
|
|
);
|
|
|
|
DrawText("Select Color", 2 + MarginSize, 10 + 256 + MarginSize, 10, Color.Black);
|
|
for (var i = 0; i < colors.Length; i++)
|
|
{
|
|
DrawRectangleRec(colorRec[i], colors[i]);
|
|
if (activeCol == i)
|
|
{
|
|
DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(Color.White, 0.5f));
|
|
}
|
|
}
|
|
|
|
DrawText("Scale (UP/DOWN to change)", 2 + MarginSize, 80 + 256 + MarginSize, 10, Color.Black);
|
|
DrawText($"{scale:F2}x", 2 + MarginSize, 92 + 256 + MarginSize, 20, Color.Black);
|
|
|
|
DrawText("Rotation (LEFT/RIGHT to change)", 2 + MarginSize, 122 + 256 + MarginSize, 10, Color.Black);
|
|
DrawText($"{rotation:F0} degrees", 2 + MarginSize, 134 + 256 + MarginSize, 20, Color.Black);
|
|
|
|
DrawText("Press [SPACE] to reset", 2 + MarginSize, 164 + 256 + MarginSize, 10, Color.DarkBlue);
|
|
|
|
// Draw FPS
|
|
DrawText($"{GetFPS()} FPS", 2 + MarginSize, 2 + MarginSize, 20, Color.Black);
|
|
EndDrawing();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
|
|
public void Unload()
|
|
{
|
|
UnloadTexture(texPattern); // Unload texture
|
|
}
|
|
|
|
public static int Main()
|
|
{
|
|
// Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
SetConfigFlags(ConfigFlags.ResizableWindow); // Make the window resizable
|
|
InitWindow(800, 450, "raylib [textures] example - tiled drawing");
|
|
|
|
SetTargetFPS(60);
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
var game = new DrawTiled();
|
|
game.Init();
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
game.Update();
|
|
}
|
|
|
|
game.Unload();
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.
|
|
private static void DrawTextureTiled(
|
|
Texture2D texture,
|
|
Rectangle source,
|
|
Rectangle dest,
|
|
Vector2 origin,
|
|
float rotation,
|
|
float scale,
|
|
Color tint
|
|
)
|
|
{
|
|
if ((texture.Id <= 0) || (scale <= 0.0f))
|
|
{
|
|
// Wanna see a infinite loop?!...just delete this line!
|
|
return;
|
|
}
|
|
|
|
if ((source.Width == 0) || (source.Height == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int tileWidth = (int)(source.Width * scale), tileHeight = (int)(source.Height * scale);
|
|
if ((dest.Width < tileWidth) && (dest.Height < tileHeight))
|
|
{
|
|
// Can fit only one tile
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)dest.Width / tileWidth) * source.Width,
|
|
((float)dest.Height / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(dest.X, dest.Y, dest.Width, dest.Height), origin, rotation, tint
|
|
);
|
|
}
|
|
else if (dest.Width <= tileWidth)
|
|
{
|
|
// Tiled vertically (one column)
|
|
var dy = 0;
|
|
for (; dy + tileHeight < dest.Height; dy += tileHeight)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)dest.Width / tileWidth) * source.Width,
|
|
source.Height
|
|
),
|
|
new Rectangle(dest.X, dest.Y + dy, dest.Width, (float)tileHeight),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
|
|
// Fit last tile
|
|
if (dy < dest.Height)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)dest.Width / tileWidth) * source.Width,
|
|
((float)(dest.Height - dy) / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(dest.X, dest.Y + dy, dest.Width, dest.Height - dy),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
}
|
|
else if (dest.Height <= tileHeight)
|
|
{
|
|
// Tiled horizontally (one row)
|
|
var dx = 0;
|
|
for (; dx + tileWidth < dest.Width; dx += tileWidth)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
source.Width,
|
|
((float)dest.Height / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(dest.X + dx, dest.Y, (float)tileWidth, dest.Height),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
|
|
// Fit last tile
|
|
if (dx < dest.Width)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)(dest.Width - dx) / tileWidth) * source.Width,
|
|
((float)dest.Height / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(
|
|
dest.X + dx,
|
|
dest.Y,
|
|
dest.Width - dx,
|
|
dest.Height
|
|
),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Tiled both horizontally and vertically (rows and columns)
|
|
var dx = 0;
|
|
for (; dx + tileWidth < dest.Width; dx += tileWidth)
|
|
{
|
|
var dy = 0;
|
|
for (; dy + tileHeight < dest.Height; dy += tileHeight)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
source,
|
|
new Rectangle(
|
|
dest.X + dx,
|
|
dest.Y + dy,
|
|
(float)tileWidth,
|
|
(float)tileHeight
|
|
),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
|
|
if (dy < dest.Height)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
source.Width,
|
|
((float)(dest.Height - dy) / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(
|
|
dest.X + dx,
|
|
dest.Y + dy,
|
|
(float)tileWidth, dest.Height - dy
|
|
),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
}
|
|
|
|
// Fit last column of tiles
|
|
if (dx < dest.Width)
|
|
{
|
|
var dy = 0;
|
|
for (; dy + tileHeight < dest.Height; dy += tileHeight)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)(dest.Width - dx) / tileWidth) * source.Width,
|
|
source.Height
|
|
),
|
|
new Rectangle(
|
|
dest.X + dx,
|
|
dest.Y + dy,
|
|
dest.Width - dx,
|
|
(float)tileHeight
|
|
),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
|
|
// Draw final tile in the bottom right corner
|
|
if (dy < dest.Height)
|
|
{
|
|
DrawTexturePro(
|
|
texture,
|
|
new Rectangle(
|
|
source.X,
|
|
source.Y,
|
|
((float)(dest.Width - dx) / tileWidth) * source.Width,
|
|
((float)(dest.Height - dy) / tileHeight) * source.Height
|
|
),
|
|
new Rectangle(
|
|
dest.X + dx,
|
|
dest.Y + dy,
|
|
dest.Width - dx,
|
|
dest.Height - dy
|
|
),
|
|
origin,
|
|
rotation,
|
|
tint
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|