Add project files.

This commit is contained in:
Royce551 2022-07-31 14:02:50 -05:00
parent c88b0fa169
commit d541472bf7
35 changed files with 1312 additions and 0 deletions

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.303",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-mac"
]
}
}
}

BIN
TestGame/Assets/Chiruuu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

View file

@ -0,0 +1,15 @@
#----------------------------- Global Properties ----------------------------#
/outputDir:bin/$(Platform)
/intermediateDir:obj/$(Platform)
/platform:DesktopGL
/config:
/profile:Reach
/compress:False
#-------------------------------- References --------------------------------#
#---------------------------------- Content ---------------------------------#

40
TestGame/Game1.cs Normal file
View file

@ -0,0 +1,40 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using TestGame.Screens;
using Water;
namespace TestGame
{
public class Game1 : WaterGame
{
public override string ProjectName => "nyaa";
public Game1() : base()
{
}
protected override void Initialize()
{
Screen.ChangeScreen(new TestScreen(Screen.Game, Screen, Window));
base.Initialize();
}
protected override void LoadContent()
{
base.LoadContent();
}
protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
}
}
}

BIN
TestGame/Icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

BIN
TestGame/Icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

3
TestGame/Program.cs Normal file
View file

@ -0,0 +1,3 @@

using var game = new TestGame.Game1();
game.Run();

View file

@ -0,0 +1,35 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Graphics;
using Water.Graphics.Controls;
using Water.Graphics.Screens;
namespace TestGame.Screens
{
public class TestScreen : Screen
{
public TestScreen(GameObjectManager game, GameObjectScreen screen, GameWindow window)
{
var box = new Sprite(new(10, 10, 1280, 720), "Assets/Chiruuu.png")
{
Layout = Layout.Center
};
screen.AddChild(game.AddObject(box));
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
}
public override void Update(GameTime gameTime)
{
}
}
}

37
TestGame/TestGame.csproj Normal file
View file

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RollForward>Major</RollForward>
<PublishReadyToRun>false</PublishReadyToRun>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="Icon.ico" />
<None Remove="Icon.bmp" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Icon.bmp" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Water\Water.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\Chiruuu.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High" />
<Exec Command="dotnet tool restore" />
</Target>
</Project>

43
TestGame/app.manifest Normal file
View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="TestGame"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on and is
is designed to work with. Uncomment the appropriate elements and Windows will
automatically selected the most compatible environment. -->
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

31
Water.sln Normal file
View file

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32505.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestGame", "TestGame\TestGame.csproj", "{A285BC3A-73E1-4C87-8CFC-292481C6FFCF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Water", "Water\Water.csproj", "{6686F14D-233B-4EA7-9F9C-026D3C3F0576}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A285BC3A-73E1-4C87-8CFC-292481C6FFCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A285BC3A-73E1-4C87-8CFC-292481C6FFCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A285BC3A-73E1-4C87-8CFC-292481C6FFCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A285BC3A-73E1-4C87-8CFC-292481C6FFCF}.Release|Any CPU.Build.0 = Release|Any CPU
{6686F14D-233B-4EA7-9F9C-026D3C3F0576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6686F14D-233B-4EA7-9F9C-026D3C3F0576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6686F14D-233B-4EA7-9F9C-026D3C3F0576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6686F14D-233B-4EA7-9F9C-026D3C3F0576}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F4395309-302A-4631-91D6-E7F82F81F79D}
EndGlobalSection
EndGlobal

Binary file not shown.

View file

@ -0,0 +1,15 @@
#----------------------------- Global Properties ----------------------------#
/outputDir:bin/$(Platform)
/intermediateDir:obj/$(Platform)
/platform:DesktopGL
/config:
/profile:Reach
/compress:False
#-------------------------------- References --------------------------------#
#---------------------------------- Content ---------------------------------#

171
Water/Graphics/Container.cs Normal file
View file

@ -0,0 +1,171 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Graphics.Screens;
namespace Water.Graphics
{
public class Container : IContainer
{
public Rectangle ActualPosition { get; set; }
public Rectangle RelativePosition { get; set; }
public IContainer Parent { get; set; }
public List<IContainer> Children { get; private set; } = new();
public Layout Layout { get; set; } = Layout.Manual;
public int Margin { get; set; } = 0;
public virtual void AddChild(IContainer child)
{
child.Parent = this;
Children.Add(child);
}
public virtual void CalculateChildrenPositions()
{
foreach (var child in Children)
{
if (this is GameObjectScreen)
Console.WriteLine("it's true!");
var parentPosition = child.Parent?.ActualPosition ?? RelativePosition; // can be null for the root object
Rectangle tempForDebugging = child.Layout switch
{
Layout.AnchorLeft => new
(
parentPosition.X + child.Margin,
parentPosition.Y + ((parentPosition.Height - child.RelativePosition.Height) / 2),
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorTopLeft => new
(
parentPosition.X + child.Margin,
parentPosition.Y + child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorTop => new
(
parentPosition.X + ((parentPosition.Width - child.RelativePosition.Width) / 2),
parentPosition.Y + child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorTopRight => new
(
parentPosition.Right - child.RelativePosition.Width - child.Margin,
parentPosition.Y + child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorRight => new
(
parentPosition.Right - child.RelativePosition.Width - child.Margin,
parentPosition.Y + ((parentPosition.Height - child.RelativePosition.Height) / 2),
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorBottomRight => new
(
parentPosition.Right - child.RelativePosition.Width - child.Margin,
parentPosition.Bottom - child.RelativePosition.Height - child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorBottom => new
(
parentPosition.X + ((parentPosition.Width - child.RelativePosition.Width) / 2),
parentPosition.Bottom - child.RelativePosition.Height - child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.AnchorBottomLeft => new
(
parentPosition.X + child.Margin,
parentPosition.Bottom - child.RelativePosition.Height - child.Margin,
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.Center => new
(
parentPosition.X + ((parentPosition.Width - child.RelativePosition.Width) / 2),
parentPosition.Y + ((parentPosition.Height - child.RelativePosition.Height) / 2),
child.RelativePosition.Width,
child.RelativePosition.Height
),
Layout.Fill => new
(
parentPosition.X + child.Margin,
parentPosition.Y + child.Margin,
parentPosition.Width - (child.Margin * 2),
parentPosition.Height - (child.Margin * 2)
),
Layout.AspectRatioMaintainingFill => new
(
new Point
(
parentPosition.X + child.Margin,
parentPosition.Y + child.Margin
),
CalculateAspectRatioMaintainingFill(parentPosition, child.RelativePosition)
),
Layout.DockLeft => new
(
parentPosition.X + child.Margin,
parentPosition.Y + child.Margin,
child.RelativePosition.Width,
parentPosition.Height - (child.Margin * 2)
),
Layout.DockTop => new
(
parentPosition.X + child.Margin,
parentPosition.Y + child.Margin,
parentPosition.Width - (child.Margin * 2),
child.RelativePosition.Height
),
Layout.DockRight => new
(
parentPosition.Right - child.RelativePosition.Width - child.Margin,
parentPosition.Y + child.Margin,
child.RelativePosition.Width,
parentPosition.Height - (child.Margin * 2)
),
Layout.DockBottom => new
(
parentPosition.X + child.Margin,
parentPosition.Bottom - child.RelativePosition.Height - child.Margin,
parentPosition.Width - (child.Margin * 2),
child.RelativePosition.Height
),
Layout.Manual or _ => new
(
parentPosition.X + child.RelativePosition.X,
parentPosition.Y + child.RelativePosition.Y,
child.RelativePosition.Width,
child.RelativePosition.Height
)
};
child.ActualPosition = tempForDebugging;
child.CalculateChildrenPositions();
}
}
private Point CalculateAspectRatioMaintainingFill(Rectangle parentPosition, Rectangle childPosition)
{
float parentAspectRatio = parentPosition.Width / parentPosition.Height;
float childAspectRatio = childPosition.Width / childPosition.Height;
float scalingFactor;
if (parentAspectRatio > childAspectRatio) scalingFactor = parentPosition.Width / childPosition.Width;
else scalingFactor = parentPosition.Height / childPosition.Height;
return new((int)(childPosition.Width * scalingFactor), (int)(childPosition.Height * scalingFactor));
}
}
}

View file

@ -0,0 +1,40 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Graphics;
namespace Water.Graphics.Containers
{
public class GridContainer : Container
{
public new IContainer[,] Children { get; set; }
public void AddChild(IContainer child, int x, int y)
{
child.Parent = this;
Children[x, y] = child;
}
public override void CalculateChildrenPositions()
{
var itemWidth = ActualPosition.Width / Children.GetLength(0);
var itemHeight = ActualPosition.Height / Children.GetLength(1);
for (int x = 0; x < Children.GetLength(0); x++)
{
for (int y = 0; y < Children.GetLength(1); y++)
{
var child = Children[x, y];
var newX = itemWidth * x;
var newY = itemHeight * y;
child.ActualPosition = new(ActualPosition.X + newX, ActualPosition.Y + newY, itemWidth, itemHeight);
child.CalculateChildrenPositions();
}
}
}
}
}

View file

@ -0,0 +1,39 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Graphics;
namespace Water.Graphics.Containers
{
public class StackContainer : Container
{
public Orientation Orientation { get; set; }
public override void CalculateChildrenPositions()
{
int offset;
if (Children.Count <= 0) return;
if (Orientation == Orientation.Horizontal) offset = ActualPosition.Width / Children.Count;
else offset = ActualPosition.Height / Children.Count;
int i = 0;
foreach (var child in Children)
{
var newPosition = offset * i;
if (Orientation == Orientation.Horizontal) child.ActualPosition = new(ActualPosition.X + newPosition, ActualPosition.Y, offset, ActualPosition.Height);
else child.ActualPosition = new(ActualPosition.X, ActualPosition.Y + newPosition, ActualPosition.Width, offset);
child.CalculateChildrenPositions();
i++;
}
}
}
public enum Orientation
{
Horizontal,
Vertical
}
}

View file

@ -0,0 +1,37 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Controls
{
public class Box : GameObject
{
private Color color;
private Texture2D sprite;
public Box(Rectangle relativePosition, Color color)
{
RelativePosition = relativePosition;
this.color = color;
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
spriteBatch.Draw(sprite, ActualPosition, color);
}
public override void Initialize()
{
sprite = new Texture2D(Game.GraphicsDevice, 1, 1);
sprite.SetData(new[] { Color.White });
}
public override void Update(GameTime gameTime)
{
}
}
}

View file

@ -0,0 +1,36 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Controls
{
public class Sprite : GameObject
{
private Texture2D sprite;
private readonly string path;
public Sprite(Rectangle relativePosition, string path)
{
RelativePosition = relativePosition;
this.path = path;
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
spriteBatch.Draw(sprite, ActualPosition, Color.White);
}
public override void Initialize()
{
sprite = Game.Textures.Get(path);
}
public override void Update(GameTime gameTime)
{
}
}
}

View file

@ -0,0 +1,194 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SpriteFontPlus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Controls
{
public class TextBlock : GameObject
{
public TextWrapMode TextWrapping;
public HorizontalTextAlignment HorizontalTextAlignment;
public VerticalTextAlignment VerticalTextAlignment;
public string Text { get; set; }
public const string LineSeparator = "|n";
private DynamicSpriteFont font;
private Color color;
// code taken from https://github.com/redteam-os/thundershock/blob/master/src/Thundershock/Gui/Elements/TextBlock.cs
public TextBlock(Rectangle relativePosition, DynamicSpriteFont font, string text, Color color)
{
RelativePosition = relativePosition;
this.color = color;
this.font = font;
Text = text;
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
var finalText = TextWrapping switch // TODO: don't calculate this EVERY frame
{
TextWrapMode.LetterWrap => LetterWrap(font, Text, ActualPosition.Width),
TextWrapMode.WordWrap => WordWrap(font, Text, ActualPosition.Width),
TextWrapMode.None or _ => Text
};
var pos = new Vector2(ActualPosition.X, ActualPosition.Y);
var lines = finalText.Split(LineSeparator);
int i = 0;
foreach (var line in lines)
{
pos.X = ActualPosition.X;
//pos.Y = ActualPosition.Y + (20 * i);
var m = font.MeasureString(line.Trim());
if (HorizontalTextAlignment != HorizontalTextAlignment.Left)
{
if (HorizontalTextAlignment == HorizontalTextAlignment.Right)
pos.X += ActualPosition.Right - m.X;
else if (HorizontalTextAlignment == HorizontalTextAlignment.Center)
pos.X += (ActualPosition.Width - m.X) / 2;
}
if (VerticalTextAlignment != VerticalTextAlignment.Top)
{
if (VerticalTextAlignment == VerticalTextAlignment.Bottom)
pos.Y = (ActualPosition.Bottom - (m.Y * ((lines.Length - i) + 1)));
else if (VerticalTextAlignment == VerticalTextAlignment.Center)
pos.Y += (ActualPosition.Height - m.Y) / 2;
}
spriteBatch.DrawString(font, line, pos, color);
pos.Y += 20; // space between lines TODO: base this off of the font or make this configurable
i++;
}
}
public override void Initialize()
{
}
public override void Update(GameTime gameTime)
{
}
private string LetterWrap(DynamicSpriteFont font, string text, float wrapWidth)
{
if (wrapWidth <= 0)
return text;
var lineWidth = 0f;
var sb = new StringBuilder();
foreach (var ch in text)
{
var m = font.MeasureString(ch.ToString()).X;
if (lineWidth + m > wrapWidth)
{
sb.Append(LineSeparator);
lineWidth = 0;
}
lineWidth += m;
sb.Append(ch);
}
return sb.ToString();
}
private string WordWrap(DynamicSpriteFont font, string text, float wrapWidth)
{
if (wrapWidth <= 0)
return text;
// first step: break words.
var words = new List<string>();
var w = "";
for (var i = 0; i <= text.Length; i++)
{
if (i < text.Length)
{
var ch = text[i];
w += ch;
if (char.IsWhiteSpace(ch))
{
words.Add(w);
w = "";
}
}
else
{
if (!string.IsNullOrEmpty(w))
{
words.Add(w);
}
}
}
// step 2: Line-wrap.
var sb = new StringBuilder();
var lineWidth = 0f;
for (var i = 0; i < words.Count; i++)
{
var word = words[i];
var m = font.MeasureString(word).X;
var m2 = font.MeasureString(word.Trim()).X;
if (lineWidth + m2 > wrapWidth && lineWidth > 0) // this makes the whole thing a lot less greedy
{
sb.Append(LineSeparator);
lineWidth = 0;
}
if (m > lineWidth)
{
var letterWrapped = LetterWrap(font, word, wrapWidth);
var lines = letterWrapped.Split(LineSeparator);
var last = lines.Last();
m = font.MeasureString(last).X;
word = last;
sb.Append(letterWrapped);
}
else
{
sb.Append(word);
}
if (word.EndsWith(LineSeparator))
lineWidth = 0;
else
lineWidth += m;
}
return sb.ToString();
}
}
public enum TextWrapMode
{
None,
LetterWrap,
WordWrap
}
public enum HorizontalTextAlignment
{
Left,
Center,
Right
}
public enum VerticalTextAlignment
{
Top,
Center,
Bottom
}
}

View file

@ -0,0 +1,29 @@
using Microsoft.Xna.Framework.Graphics;
using SpriteFontPlus;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics
{
public class FontCache
{
public Dictionary<string, DynamicSpriteFont> Cache { get; private set; } = new();
public void Add(string path, int fontSize) => Cache.Add(path, DynamicSpriteFont.FromTtf(File.ReadAllBytes(path), fontSize));
public DynamicSpriteFont Get(string path, int fontSize)
{
DynamicSpriteFont font;
if (!Cache.TryGetValue(path, out font))
{
Add(path, fontSize);
font = Get(path, fontSize);
}
return font;
}
}
}

View file

@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
namespace Water.Graphics
{
public abstract class GameObject : Container, IDrawableThing
{
public GameObjectManager Game { get; set; }
public abstract void Initialize();
public abstract void Update(GameTime gameTime);
public abstract void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice);
}
}

View file

@ -0,0 +1,58 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
namespace Water.Graphics
{
public class GameObjectManager : IDrawableThing
{
public List<GameObject> AllObjects { get; private set; } = new();
public GraphicsDevice GraphicsDevice { get; }
public TextureCache Textures { get; private set; }
public FontCache Fonts { get; private set; }
public float GameSpeed { get; set; } = 100f;
private readonly List<GameObject> objectsToAdd = new();
private readonly List<GameObject> objectsToRemove = new();
public GameObjectManager(GraphicsDevice graphicsDevice)
{
GraphicsDevice = graphicsDevice;
Textures = new TextureCache(graphicsDevice);
Fonts = new FontCache();
}
public GameObject AddObject(GameObject obj)
{
obj.Game = this;
obj.Initialize();
objectsToAdd.Add(obj);
return obj;
}
public GameObject RemoveObject(GameObject obj)
{
objectsToRemove.Add(obj);
return obj;
}
public void Update(GameTime gameTime)
{
foreach (var obj in AllObjects)
obj.Update(gameTime);
AllObjects.AddRange(objectsToAdd);
objectsToAdd.Clear();
foreach (var obj in objectsToRemove)
{
AllObjects.Remove(obj);
}
objectsToRemove.Clear();
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
foreach (var obj in AllObjects)
obj.Draw(gameTime, spriteBatch, graphicsDevice);
}
}
}

View file

@ -0,0 +1,41 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics
{
public interface IContainer
{
public Rectangle ActualPosition { get; set; }
public Rectangle RelativePosition { get; set; }
public IContainer Parent { get; set; }
public Layout Layout { get; set; }
public int Margin { get; set; }
public void CalculateChildrenPositions();
}
public enum Layout
{
Manual,
AnchorLeft,
AnchorTopLeft,
AnchorTop,
AnchorTopRight,
AnchorRight,
AnchorBottomRight,
AnchorBottom,
AnchorBottomLeft,
Center,
DockLeft,
DockTop,
DockRight,
DockBottom,
Fill,
AspectRatioMaintainingFill
}
}

View file

@ -0,0 +1,14 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
namespace Water.Graphics
{
public interface IDrawableThing
{
public void Update(GameTime gameTime);
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice);
}
}

View file

@ -0,0 +1,23 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Screens
{
public class DefaultScreen : Screen
{
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
}
public override void Update(GameTime gameTime)
{
}
}
}

View file

@ -0,0 +1,66 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Screens
{
public class GameObjectScreen : GameObject
{
public List<Screen> Screens { get; } = new();
public bool HasScreens { get => Screens.Count > 0; }
public void AddScreen(Screen screen)
{
Screens.Add(screen);
}
public void RemoveScreen(Screen screen)
{
Screens.Remove(screen);
}
public void RemoveAllScreens()
{
Screens.Clear();
}
public override void AddChild(IContainer child)
{
base.AddChild(child);
CalculateChildrenPositions();
}
public override void Initialize()
{
}
public void ChangeScreen(Screen screen)
{
RemoveAllScreens();
AddScreen(screen);
}
public override void Update(GameTime gameTime)
{
if (!HasScreens) return;
foreach (var screen in Screens)
screen.Update(gameTime);
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
if (!HasScreens) return;
foreach (var screen in Screens)
screen.Draw(gameTime, spriteBatch, graphicsDevice);
}
public void UpdateScreenSize(Rectangle newSize)
{
ActualPosition = newSize;
RelativePosition = newSize;
CalculateChildrenPositions();
}
}
}

View file

@ -0,0 +1,16 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics.Screens
{
public abstract class Screen : IDrawableThing
{
public abstract void Update(GameTime gameTime);
public abstract void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice);
}
}

View file

@ -0,0 +1,30 @@
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Graphics
{
public class TextureCache
{
public Dictionary<string, Texture2D> Cache { get; private set; } = new();
private readonly GraphicsDevice graphicsDevice;
public TextureCache(GraphicsDevice graphicsDevice) => this.graphicsDevice = graphicsDevice;
public void AddTexture(string path) => Cache.Add(path, Texture2D.FromFile(graphicsDevice, path));
public Texture2D Get(string path)
{
Texture2D texture;
if (!Cache.TryGetValue(path, out texture))
{
texture = Texture2D.FromFile(graphicsDevice, path);
Cache.Add(path, texture);
}
return texture;
}
}
}

BIN
Water/Icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

BIN
Water/Icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

14
Water/Program.cs Normal file
View file

@ -0,0 +1,14 @@
using System;
namespace Water
{
public static class Program
{
[STAThread]
static void Main()
{
using (var game = new WaterGame())
game.Run();
}
}
}

View file

@ -0,0 +1,64 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Graphics;
using Water.Graphics.Containers;
using Water.Graphics.Controls;
using Water.Graphics.Screens;
namespace Water.Screens
{
public class DebugOverlay : Screen
{
private GameObjectManager game;
private GameWindow window;
private double updatesPerSecond;
private double drawsPerSecond;
private TextBlock updateText;
public DebugOverlay(GameObjectManager game, GameObjectScreen screen, GameWindow window)
{
this.game = game;
this.window = window;
var box = new Box(new(0, 0, 500, 200), new(255, 255, 255, 100));
box.Layout = Layout.AnchorTop;
updateText = new(new(0, 0, 10, 10), game.Fonts.Get("Assets/IBMPLEXSANS-MEDIUM.TTF", 15), "updates per second", Color.Gray)
{
Layout = Layout.Fill,
TextWrapping = TextWrapMode.WordWrap,
HorizontalTextAlignment = HorizontalTextAlignment.Center,
VerticalTextAlignment = VerticalTextAlignment.Top
};
screen.AddChild(box);
game.AddObject(updateText);
box.AddChild(updateText);
}
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
drawsPerSecond = 1 / gameTime.ElapsedGameTime.TotalSeconds;
}
public override void Update(GameTime gameTime)
{
var intersectedObjects = new List<string>();
foreach (var obj in game.AllObjects)
{
if (new Rectangle(Mouse.GetState(window).Position, new(10, 10)).Intersects(obj.ActualPosition))
{
intersectedObjects.Add($"{obj.ToString().ToUpper()} Actual: {obj.ActualPosition} Relative: {obj.RelativePosition} Parent: {obj.Parent} Children: {string.Join(',', obj.Children)}");
}
}
updatesPerSecond = 1 / gameTime.ElapsedGameTime.TotalSeconds;
updateText.Text = $"{Math.Round(updatesPerSecond, 2)} updates per second|n{Math.Round(drawsPerSecond, 2)} draws per second|n{game.AllObjects.Count} objects|n{string.Join("|n", intersectedObjects)}";
}
}
}

36
Water/Water.csproj Normal file
View file

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net5.0</TargetFramework>
<PublishReadyToRun>false</PublishReadyToRun>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="Icon.ico" />
<None Remove="Icon.bmp" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Icon.bmp" />
</ItemGroup>
<ItemGroup>
<MonoGameContentReference Include="Content\Content.mgcb" />
</ItemGroup>
<ItemGroup>
<TrimmerRootAssembly Include="Microsoft.Xna.Framework.Content.ContentTypeReader" Visible="false" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.0.1641" />
<PackageReference Include="SpriteFontPlus.MonoGame" Version="0.7.0.22" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\IBMPLEXSANS-MEDIUM.TTF">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

88
Water/WaterGame.cs Normal file
View file

@ -0,0 +1,88 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Water.Graphics;
using Water.Graphics.Screens;
using Water.Screens;
namespace Water
{
public class WaterGame : Game
{
public GameObjectScreen Screen { get; private set; }
public virtual string ProjectName { get; }
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private GameObjectManager gameObjectManager;
public WaterGame()
{
_graphics = new GraphicsDeviceManager(this);
_graphics.PreferredBackBufferWidth = 1600;
_graphics.PreferredBackBufferHeight = 900;
_graphics.SynchronizeWithVerticalRetrace = false;
Window.AllowUserResizing = true;
IsFixedTimeStep = false;
//TargetElapsedTime = TimeSpan.FromMilliseconds(8.33);
_graphics.ApplyChanges();
Screen = new();
Screen.RelativePosition = GraphicsDevice.Viewport.Bounds;
Window.ClientSizeChanged += Window_ClientSizeChanged;
gameObjectManager = new(GraphicsDevice);
Screen.ChangeScreen(new DefaultScreen());
gameObjectManager.AddObject(Screen);
Screen.UpdateScreenSize(new(0, 0, Window.ClientBounds.Width, Window.ClientBounds.Height));
Content.RootDirectory = "Content";
IsMouseVisible = true;
#if DEBUG
Screen.AddScreen(new DebugOverlay(gameObjectManager, Screen, Window));
#endif
}
private void Window_ClientSizeChanged(object sender, EventArgs e)
{
Screen.UpdateScreenSize(new(0, 0, Window.ClientBounds.Width, Window.ClientBounds.Height));
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// TODO: Add your update logic here
gameObjectManager.Update(gameTime);
Window.Title = ProjectName ?? "Water Engine";
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
// TODO: Add your drawing code here
_spriteBatch.Begin();
gameObjectManager.Draw(gameTime, _spriteBatch, GraphicsDevice);
_spriteBatch.End();
base.Draw(gameTime);
}
}
}

43
Water/app.manifest Normal file
View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="Water"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on and is
is designed to work with. Uncomment the appropriate elements and Windows will
automatically selected the most compatible environment. -->
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>