added shadow, render layering and ores

This commit is contained in:
MasterGordon 2022-11-23 17:00:33 +01:00
parent 961725d459
commit 2223e32994
26 changed files with 305 additions and 38 deletions

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<PublishTrimmed>true</PublishTrimmed>
<TrimmerDefaultAction>link</TrimmerDefaultAction>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

After

Width:  |  Height:  |  Size: 338 B

BIN
Mine2d/assets/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
Mine2d/assets/ore1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

BIN
Mine2d/assets/ore2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

BIN
Mine2d/assets/ore3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

BIN
Mine2d/assets/ore4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 431 B

View File

@ -1,4 +1,5 @@
using Mine2d.backend.data;
using Mine2d.core;
using Mine2d.core.data;
using Mine2d.engine.system.annotations;
@ -11,28 +12,42 @@ public class Breaking
public static void TickHybrid()
{
var ctx = Context.Get();
ctx.GameState.Players.ForEach(player =>
foreach (var player in ctx.GameState.Players)
{
player.MiningCooldown = Math.Max(0, player.MiningCooldown - 1);
if (player.Mining == Vector2.Zero)
{
player.MiningCooldown = Math.Max(0, player.MiningCooldown - 1);
if (player.Mining != Vector2.Zero && player.MiningCooldown == 0 && ctx.GameState.World.HasChunkAt(player.Mining))
continue;
}
if (player.MiningCooldown > 0)
{
continue;
}
if ((player.GetCenter() - player.Mining).LengthSquared() > Constants.BreakDistance * Constants.BreakDistance)
{
continue;
}
if (ctx.GameState.World.HasChunkAt(player.Mining))
{
var chunk = ctx.GameState.World.GetChunkAt(player.Mining);
var tile = chunk.GetTileAt(player.Mining);
var tileId = tile.Id;
if (tileId != 0)
{
var chunk = ctx.GameState.World.GetChunkAt(player.Mining);
var tile = chunk.GetTileAt(player.Mining);
var tileId = tile.Id;
if (tileId != 0)
player.MiningCooldown = 10;
var tileRegistry = ctx.TileRegistry;
var hardness = tileRegistry.GetTile(tileId).Hardness;
chunk.SetTileAt(player.Mining, tile with { Hits = tile.Hits + 1 });
if (tile.Hits >= hardness)
{
player.MiningCooldown = 10;
var tileRegistry = ctx.TileRegistry;
var hardness = tileRegistry.GetTile(tileId).Hardness;
chunk.SetTileAt(player.Mining, tile with { Hits = tile.Hits + 1 });
if (tile.Hits >= hardness)
{
chunk.SetTileAt(player.Mining, STile.From(0));
}
chunk.SetTileAt(player.Mining, STile.From(0));
}
}
}
);
}
}
[Interaction(InteractorKind.Server, "break")]

View File

@ -19,7 +19,7 @@ public class Connect
{
Name = packet.PlayerName,
Id = packet.PlayerGuid,
Position = new Vector2(32 * 16 * 1000 + 16 * 14.5f, 32 * 16 * 10 + 16 * 14.5f),
Position = new Vector2(512244, 5390),
Movement = new Vector2(0, 0)
}
);

View File

@ -14,7 +14,11 @@ public class WorldGeneration
new Vector2(0, 1),
new Vector2(1, 0),
new Vector2(0, -1),
new Vector2(-1, 0)
new Vector2(-1, 0),
new Vector2(1, 1),
new Vector2(1, -1),
new Vector2(-1, -1),
new Vector2(-1, 1)
};
[Interaction(InteractorKind.Server, "playerMoved")]
@ -30,7 +34,7 @@ public class WorldGeneration
if (!hasChunkGenerated)
{
var chunkPos = World.ToChunkPos(generationTarget);
world.AddChunk(ChunkGenerator.CreateFilledChunk((int)chunkPos.X, (int)chunkPos.Y, STile.From(Tiles.Stone)));
world.AddChunk(ChunkGenerator.CreateChunk((int)chunkPos.X, (int)chunkPos.Y));
}
}
}

View File

@ -4,5 +4,6 @@ public class Constants
{
public const int ChunkSize = 32;
public const int TileSize = 16;
public const int BreakDistance = 64;
public static Vector2 Gravity = new(0, 0.1f);
}

View File

@ -31,8 +31,10 @@ public class PlayerEntity
do
{
var pL = p.Position + new Vector2(0, -8);
var pL2 = p.Position + new Vector2(0, -24);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL);
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL)
|| world.HasChunkAt(pL2) && world.GetChunkAt(pL2).HasTileAt(pL2);
if (hasCollision)
{
p.Movement = p.Movement with { X = 0 };
@ -41,9 +43,11 @@ public class PlayerEntity
} while (hasCollision);
do
{
var pR = p.Position + new Vector2(16, -8);
var pR = p.Position + new Vector2(14, -8);
var pR2 = p.Position + new Vector2(14, -24);
hasCollision =
world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);
world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR)
|| world.HasChunkAt(pR2) && world.GetChunkAt(pR2).HasTileAt(pR2);
if (hasCollision)
{
p.Movement = p.Movement with { X = 0 };
@ -52,8 +56,8 @@ public class PlayerEntity
} while (hasCollision);
do
{
var pL = p.Position;
var pR = p.Position + new Vector2(16, 0);
var pL = p.Position + new Vector2(0, 0);
var pR = p.Position + new Vector2(14, 0);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);
@ -65,8 +69,8 @@ public class PlayerEntity
} while (hasCollision);
do
{
var pL = p.Position + new Vector2(0, -32);
var pR = p.Position + new Vector2(16, -32);
var pL = p.Position + new Vector2(0, -28);
var pR = p.Position + new Vector2(14, -28);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);

View File

@ -0,0 +1,10 @@
using Mine2d.engine;
namespace Mine2d.core.tiles;
public class OreTile : Tile
{
public OreTile(string name, string[] texturePath, int hardness) : base(name, new TextureFactory().CreateTexture(texturePath), hardness)
{
}
}

View File

@ -1,4 +1,5 @@
using Mine2d.core.data;
using Mine2d.engine;
namespace Mine2d.core.tiles;
@ -26,6 +27,13 @@ public class Tile
SDL_FreeSurface(surface);
}
public Tile(string name, IntPtr texture, int hardness)
{
this.Name = name;
this.Hardness = hardness;
this.texture = texture;
}
~Tile()
{
SDL_DestroyTexture(this.texture);
@ -36,6 +44,7 @@ public class Tile
var renderer = Context.Get().Renderer;
var scale = Context.Get().FrontendGameState.Settings.GameScale;
var camera = Context.Get().FrontendGameState.Camera;
Renderer.SetTextureColorModulate(this.texture, 255);
renderer.DrawTexture(
this.texture,
(x - (int)camera.Position.X) * scale,

View File

@ -3,6 +3,10 @@ namespace Mine2d.core.tiles;
public enum Tiles
{
Stone = 1,
Ore1 = 2,
Ore2 = 3,
Ore3 = 4,
Ore4 = 5,
}
public class TileRegistry
@ -12,6 +16,10 @@ public class TileRegistry
public void RegisterTile()
{
this.Tiles.Add(1, new Tile("stone", "stone", 5));
this.Tiles.Add(2, new OreTile("ore1", new string[] { "stone", "ore1" }, 5));
this.Tiles.Add(3, new OreTile("ore2", new string[] { "stone", "ore2" }, 7));
this.Tiles.Add(4, new OreTile("ore3", new string[] { "stone", "ore3" }, 8));
this.Tiles.Add(5, new OreTile("ore4", new string[] { "stone", "ore4" }, 10));
}
public Tile GetTile(int id)

View File

@ -18,6 +18,43 @@ public class ChunkGenerator
return chunk;
}
public static Chunk CreateChunk(int x, int y)
{
var fill = new STile
{
Id = (int)Tiles.Stone,
};
var chunk = new Chunk(x, y);
for (var i = 0; i < Constants.ChunkSize; i++)
{
for (var j = 0; j < Constants.ChunkSize; j++)
{
if (new Random().Next(0, 100) < 10)
{
fill.Id = (int)Tiles.Ore1;
}
else if (new Random().Next(0, 100) < 10)
{
fill.Id = (int)Tiles.Ore2;
}
else if (new Random().Next(0, 100) < 10)
{
fill.Id = (int)Tiles.Ore3;
}
else if (new Random().Next(0, 100) < 10)
{
fill.Id = (int)Tiles.Ore4;
}
else
{
fill.Id = (int)Tiles.Stone;
}
chunk.SetTile(i, j, fill);
}
}
return chunk;
}
public static Chunk CreateSpawnChunk(int x, int y)
{
var chunk = new Chunk(x, y);

View File

@ -159,6 +159,16 @@ public class Renderer
ProcessStatus(SDL_RenderCopy(this.renderer, texture, ref srcRect, ref rect));
}
public static void SetTextureColorModulate(IntPtr texture, int r, int g, int b)
{
ProcessStatus(SDL_SetTextureColorMod(texture, (byte)r, (byte)g, (byte)b));
}
public static void SetTextureColorModulate(IntPtr texture, int a)
{
SetTextureColorModulate(texture, a, a, a);
}
public static void ProcessStatus(int status)
{
if (status != 0)
@ -166,4 +176,10 @@ public class Renderer
throw new SDLException(SDL_GetError());
}
}
internal (int srcW, int srcH) GetTextureSize(IntPtr src)
{
ProcessStatus(SDL_QueryTexture(src, out _, out _, out var srcW, out var srcH));
return (srcW, srcH);
}
}

View File

@ -0,0 +1,52 @@
namespace Mine2d.engine;
public class TextureFactory
{
private readonly ResourceLoader resourceLoader;
private readonly Renderer renderer;
private readonly Dictionary<string, IntPtr> textureCache = new();
public TextureFactory()
{
this.resourceLoader = Context.Get().ResourceLoader;
this.renderer = Context.Get().Renderer;
}
public IntPtr CreateTexture(string[] path)
{
var target = this.loadTexture(path[0]);
for (var i = 1; i < path.Length; i++)
{
var t = this.loadTexture(path[i]);
target = this.mergeTextures(target, t);
}
return target;
}
private IntPtr mergeTextures(IntPtr t1, IntPtr t2)
{
var (w, h) = this.renderer.GetTextureSize(t1);
var target = SDL_CreateTexture(this.renderer.GetRaw(), SDL_PIXELFORMAT_RGBA8888, (int)SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, w, h);
Renderer.ProcessStatus(SDL_SetRenderTarget(this.renderer.GetRaw(), target));
Renderer.ProcessStatus(SDL_SetTextureBlendMode(target, SDL_BlendMode.SDL_BLENDMODE_BLEND));
this.renderer.DrawTexture(t1, 0, 0, w, h);
this.renderer.DrawTexture(t2, 0, 0, w, h);
Renderer.ProcessStatus(SDL_SetRenderTarget(this.renderer.GetRaw(), IntPtr.Zero));
return target;
}
private IntPtr loadTexture(string path)
{
if (this.textureCache.TryGetValue(path, out var value))
{
return value;
}
var (ptr, size) = this.resourceLoader.LoadToIntPtr("assets." + path + ".png");
var sdlBuffer = SDL_RWFromMem(ptr, size);
var surface = IMG_Load_RW(sdlBuffer, 1);
var texture = this.renderer.CreateTextureFromSurface(surface);
SDL_FreeSurface(surface);
this.textureCache.Add(path, texture);
return texture;
}
}

View File

@ -12,7 +12,7 @@ public class Window
SDL_WINDOWPOS_CENTERED,
w,
h,
SDL_WindowFlags.SDL_WINDOW_VULKAN | SDL_WindowFlags.SDL_WINDOW_RESIZABLE
SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_RESIZABLE
);
}

View File

@ -0,0 +1,46 @@
namespace Mine2d.frontend.renderer;
public class BackgroundRenderer : IRenderer
{
private readonly IntPtr texture;
public BackgroundRenderer()
{
var rl = Context.Get().ResourceLoader;
var (ptr, size) = rl.LoadToIntPtr("assets.background.png");
var sdlBuffer = SDL_RWFromMem(ptr, size);
var surface = IMG_Load_RW(sdlBuffer, 1);
this.texture = Context.Get().Renderer.CreateTextureFromSurface(surface);
SDL_FreeSurface(surface);
}
~BackgroundRenderer()
{
SDL_DestroyTexture(this.texture);
}
public void Render()
{
var ctx = Context.Get();
var camera = ctx.FrontendGameState.Camera;
var scale = ctx.FrontendGameState.Settings.GameScale;
var renderer = ctx.Renderer;
var (w, h) = ctx.Window.GetSize();
var offsetX = camera.Position.X / scale % 16;
var offsetY = camera.Position.Y / scale % 16;
w /= scale;
h /= scale;
for (var x = -16; x < w + 16; x += 16)
{
for (var y = -16; y < h + 16; y += 16)
{
var rx = (x - (int)offsetX) * scale;
var ry = (y - (int)offsetY) * scale;
var rw = 16 * scale;
var rh = 16 * scale;
renderer.DrawTexture(this.texture, rx, ry, rw, rh);
}
}
}
}

View File

@ -6,6 +6,7 @@ public class GameRenderer : IRenderer
public GameRenderer()
{
this.renderers.Add(new BackgroundRenderer());
this.renderers.Add(new WorldRenderer());
this.renderers.Add(new PlayerRenderer());
this.renderers.Add(new WorldCursorRenderer());

View File

@ -20,9 +20,9 @@ public class PlayerRenderer : IRenderer
ctx.Renderer.DrawRect(
(player.Position.X - (int)camera.Position.X) * scale,
(player.Position.Y - (int)camera.Position.Y) * scale - 32 * scale,
16 * scale,
32 * scale
(player.Position.Y - (int)camera.Position.Y) * scale - 28 * scale,
14 * scale,
28 * scale
);
}
}

View File

@ -10,6 +10,11 @@ public class WorldCursorRenderer : IRenderer
var scale = ctx.FrontendGameState.Settings.GameScale;
var camera = ctx.FrontendGameState.Camera;
var absoluteMousePos = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + camera.Position;
if (PlayerEntity.GetSelf() == null || (absoluteMousePos - PlayerEntity.GetSelf().GetCenter()).LengthSquared() > Constants.BreakDistance * Constants.BreakDistance)
{
return;
}
if (ctx.GameState.World.HasTileAt((int)absoluteMousePos.X, (int)absoluteMousePos.Y))
{
var ts = Constants.TileSize;

View File

@ -1,14 +1,46 @@
using Mine2d.core;
using Mine2d.engine;
namespace Mine2d.frontend.renderer;
public class WorldRenderer : IRenderer
{
private readonly IntPtr light;
private IntPtr overlay;
private int overlayWidth, overlayHeight;
public WorldRenderer()
{
var rl = Context.Get().ResourceLoader;
var (ptr, size) = rl.LoadToIntPtr("assets.light.png");
var sdlBuffer = SDL_RWFromMem(ptr, size);
var surface = IMG_Load_RW(sdlBuffer, 1);
this.light = Context.Get().Renderer.CreateTextureFromSurface(surface);
SDL_FreeSurface(surface);
var (w, h) = Context.Get().Window.GetSize();
this.overlay = SDL_CreateTexture(Context.Get().Renderer.GetRaw(), SDL_PIXELFORMAT_RGBA8888, (int)SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, w, h);
}
public void Render()
{
var ctx = Context.Get();
var world = ctx.GameState.World;
var tileRegistry = ctx.TileRegistry;
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), this.overlay));
var (ww, wh) = ctx.Window.GetSize();
if (wh != this.overlayHeight || ww != this.overlayWidth)
{
SDL_DestroyTexture(this.overlay);
this.overlay = SDL_CreateTexture(ctx.Renderer.GetRaw(), SDL_PIXELFORMAT_RGBA8888, (int)SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, ww, wh);
this.overlayWidth = ww;
this.overlayHeight = wh;
}
clearTexture(this.overlay);
var renderer = Context.Get().Renderer;
var scale = ctx.FrontendGameState.Settings.GameScale;
var camera = Context.Get().FrontendGameState.Camera;
Renderer.ProcessStatus(SDL_SetTextureBlendMode(this.light, SDL_BlendMode.SDL_BLENDMODE_BLEND));
foreach (var (_, chunk) in world.Chunks)
{
for (var y = 0; y < Constants.ChunkSize; y++)
@ -16,17 +48,46 @@ public class WorldRenderer : IRenderer
for (var x = 0; x < Constants.ChunkSize; x++)
{
var stile = chunk.GetTile(x, y);
var chunkOffsetX = chunk.X * Constants.TileSize * Constants.ChunkSize;
var chunkOffsetY = chunk.Y * Constants.TileSize * Constants.ChunkSize;
var drawX = x * 16 + chunkOffsetX;
var drawY = y * 16 + chunkOffsetY;
if (stile.Id == 0)
{
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), this.overlay));
renderer.DrawTexture(
this.light,
(drawX - (int)camera.Position.X - 40) * scale,
(drawY - (int)camera.Position.Y - 40) * scale,
96 * scale,
96 * scale
);
continue;
}
else
{
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), IntPtr.Zero));
}
var tile = tileRegistry.GetTile(stile.Id);
var chunkOffsetX = chunk.X * Constants.TileSize * Constants.ChunkSize;
var chunkOffsetY = chunk.Y * Constants.TileSize * Constants.ChunkSize;
tile.Render(x * 16 + chunkOffsetX, y * 16 + chunkOffsetY, stile);
tile.Render(drawX, drawY, stile);
}
}
}
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), IntPtr.Zero));
Renderer.ProcessStatus(SDL_SetTextureBlendMode(this.overlay, SDL_BlendMode.SDL_BLENDMODE_MUL));
Renderer.ProcessStatus(SDL_RenderCopy(ctx.Renderer.GetRaw(), this.overlay, IntPtr.Zero, IntPtr.Zero));
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), IntPtr.Zero));
}
public static void clearTexture(IntPtr texture)
{
var ctx = Context.Get();
var renderer = ctx.Renderer;
var (w, h) = renderer.GetTextureSize(texture);
var clearRect = new SDL_Rect { x = 0, y = 0, w = w, h = h };
Renderer.ProcessStatus(SDL_SetRenderTarget(ctx.Renderer.GetRaw(), texture));
_ = SDL_SetRenderDrawColor(ctx.Renderer.GetRaw(), 0, 0, 0, 255);
Renderer.ProcessStatus(SDL_RenderFillRect(ctx.Renderer.GetRaw(), ref clearRect));
}
}

View File

@ -1,5 +1,3 @@
using Mine2d.engine;
namespace Mine2d.state;
public class Player
@ -11,8 +9,8 @@ public class Player
public Vector2 Mining { get; set; }
public int MiningCooldown { get; set; }
public Line GetBottomCollisionLine()
public Vector2 GetCenter()
{
return new Line(this.Position, this.Position + new Vector2(16, 0));
return this.Position + new Vector2(7, -14);
}
}