diff --git a/Sandbox/content/RocketGame/rocket.dds b/Sandbox/content/RocketGame/rocket.dds new file mode 100644 index 0000000..e30df3d Binary files /dev/null and b/Sandbox/content/RocketGame/rocket.dds differ diff --git a/Sandbox/src/GameLayer.bf b/Sandbox/src/GameLayer.bf new file mode 100644 index 0000000..7a91f3d --- /dev/null +++ b/Sandbox/src/GameLayer.bf @@ -0,0 +1,201 @@ +using System; +using GlitchyEngine; +using GlitchyEngine.Events; +using System.Diagnostics; +using GlitchLog; +using GlitchyEngine.ImGui; +using ImGui; +using GlitchyEngine.Renderer; +using GlitchyEngine.Math; +using GlitchyEngine.World; +using GlitchyEngine.Renderer.Text; +using System.IO; +using msdfgen; +using System.Collections; +using System.Diagnostics; + +namespace Sandbox +{ + class GameLayer : Layer + { + GraphicsContext _context ~ _?.ReleaseRef(); + + BlendState _alphaBlendState ~ _?.ReleaseRef(); + + Font _font ~ _.ReleaseRef(); + + DepthStencilState _depthStencilState ~ _.ReleaseRef(); + + Rocket _rocket ~ delete _; + + OrthographicCamera _camera ~ delete _; + + List _obstacles = new List() ~ DeleteContainerAndItems!(_); + + [AllowAppend] + public this() : base("Example") + { + Application.Get().Window.IsVSync = false; + + _context = Application.Get().Window.Context..AddRef(); + + BlendStateDescription blendDesc = .(); + blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All); + _alphaBlendState = new BlendState(blendDesc); + + _font = new Font("C:\\Windows\\Fonts\\arial.ttf", 64, true, 'A', 16); + + _camera = new .() + { + NearPlane = -10, + FarPlane = 10, + Height = 5, + Width = 5 * _context.SwapChain.AspectRatio + }..Update(); + + DepthStencilStateDescription dssDesc = .() + { + DepthEnabled = false + }; + _depthStencilState = new DepthStencilState(dssDesc); + + InitGame(); + } + + private void InitGame() + { + _rocket = new Rocket(); + _rocket.Obstacles = _obstacles; + + InitStarField(); + } + + Vector4[] points = new Vector4[1000] ~ delete _; + + Random startRandom = new .() ~ delete _; + + private void InitStarField() + { + for (int i < points.Count) + { + Vector4 v = .(); + v.X = startRandom.Next(-1000, 1000) / 2000f * _camera.Width; + v.Y = startRandom.Next(-1000, 1000) / 2000f * _camera.Height; + v.Z = startRandom.Next(0, 999) / 1000.0f; + v.W = startRandom.Next(1000, 3000) / 1000.0f; + + points[i] = v; + } + } + + private float lastObstacle = 0.0f; + + public override void Update(GameTime gameTime) + { + float screenWidth = _camera.Width; + Obstacle.ScreenWidth = screenWidth / 2.0f; + + lastObstacle += Obstacle.Speed * gameTime.DeltaTime; + + if (lastObstacle <= -Obstacle.ScreenWidth) + { + _obstacles.Add(new Obstacle()); + lastObstacle += 2.5f; + _obstacles.Back.Position.X = Obstacle.ScreenWidth + 1.0f; + _obstacles.Back.Position.Y = Obstacle.random.Next(-1000, 1000) / 1000f; + } + + for (int i < _obstacles.Count) + { + _obstacles[i].Update(gameTime); + + if (_obstacles[i].Dead) + { + delete _obstacles[i]; + + _obstacles.RemoveAt(i); + i--; + } + } + + _rocket.Update(gameTime); + Obstacle.Speed = -_rocket.FlightSpeed; + + RenderCommand.Clear(null, .Black); + + // Draw test geometry + RenderCommand.SetRenderTarget(null); + RenderCommand.BindRenderTargets(); + + RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport); + + RenderCommand.SetBlendState(_alphaBlendState); + RenderCommand.SetDepthStencilState(_depthStencilState); + + Renderer2D.BeginScene(_camera, .BackToFront); + + DrawStartField(gameTime); + + Renderer2D.EndScene(); + + Renderer2D.BeginScene(_camera, .BackToFront); + + Renderer2D.DrawQuad(Vector3(0, 2.75f, 1), Vector2(screenWidth, 1), 0, .White); + + Renderer2D.DrawQuad(Vector3(0, -2.75f, 1), Vector2(screenWidth, 1), 0, .White); + + for (Obstacle obs in _obstacles) + { + obs.Draw(); + } + + Renderer.BeginScene(_camera); + + _rocket.Draw(); + + FontRenderer.DrawText(_font, scope $"{_rocket.Score}", 0, 1.5f, 1, .Black); + FontRenderer.DrawText(_font, scope $"{_rocket.Score}", 0.1f, 1.6f, 1, .(230, 230, 230)); + + Renderer.EndScene(); + + Renderer2D.EndScene(); + } + + private void DrawStartField(GameTime gameTime) + { + for (int i < points.Count) + { + points[i].X -= _rocket.FlightSpeed * gameTime.DeltaTime * points[i].Z; + + if (points[i].X < _camera.Left) + { + points[i].X = _camera.Right + startRandom.Next(500, 1500) / 1000.0f; + } + + Renderer2D.DrawCircle(Vector3(points[i].XY, 5), .(0.01f * points[i].W), Color.White); + } + } + + public override void OnEvent(Event event) + { + EventDispatcher dispatcher = EventDispatcher(event); + + dispatcher.Dispatch(scope (e) => OnImGuiRender(e)); + + dispatcher.Dispatch(scope (e) => OnWindowResize(e)); + } + + private bool OnImGuiRender(ImGuiRenderEvent e) + { + return false; + } + + private bool OnWindowResize(WindowResizeEvent e) + { + _camera.Width = _camera.Height * e.Width / (float)e.Height; + _camera.Update(); + + return false; + } + } +} \ No newline at end of file diff --git a/Sandbox/src/Line2D.bf b/Sandbox/src/Line2D.bf new file mode 100644 index 0000000..43706e8 --- /dev/null +++ b/Sandbox/src/Line2D.bf @@ -0,0 +1,213 @@ +using System; +using GlitchyEngine.Math; +namespace Sandbox +{ + struct Line2D + { + public Vector2 Start; + public Vector2 End; + + public this(Vector2 start, Vector2 end) + { + Start = start; + End = end; + } + + /// Returns true if the given point lies on the line. + public bool OnLine(Vector2 point) + { + Vector2 startToPoint = point - Start; + Vector2 startToEnd = End - Start; + + float cross = cross(startToPoint, startToEnd); + + if(!MathHelper.IsZero(cross)) + return false; + + if(Math.Abs(startToEnd.X) >= Math.Abs(startToEnd.Y)) + { + return (startToEnd.X > 0.0f) ? + (Start.X <= point.X && point.X <= End.X) : + (End.X <= point.X && point.X <= Start.X); + } + else + { + return (startToEnd.Y > 0.0f) ? + (Start.Y <= point.Y && point.Y <= End.Y) : + (End.Y <= point.Y && point.Y <= Start.Y); + } + } + + [Inline] + private float cross(Vector2 v, Vector2 w) + { + return v.X * w.Y - v.Y * w.X; + } + + public enum LineIntersection + { + case Collinear(Line2D IntersectionLine); + case CollinearNoIntersect; + case Parallel; + case Intersection(Vector2 Point); + case None; + } + + private Result<(float Start, float End)> IntervalIntersection(float startA, float endA, float startB, float endB) + { + if(startB > endA || startA > endB) + return .Err; + else + { + float start = Math.Max(startA, startB); + float end = Math.Min(endA, endB); + + return .Ok((start, end)); + } + } + + public LineIntersection Intersects(Line2D line) + { + Vector2 A = this.Start; + Vector2 B = this.End; + Vector2 C = line.Start; + Vector2 D = line.End; + + float numR = ((A.Y-C.Y) * (D.X-C.X) - (A.X-C.X) * (D.Y-C.Y)); + float den = ((B.X-A.X) * (D.Y-C.Y)-(B.Y-A.Y) * (D.X-C.X)); + + float r = numR / den; + float s = ((A.Y-C.Y) * (B.X-A.X) - (A.X-C.X) * (B.Y-A.Y)) / den; + + if((0 <= r && r <= 1) && (0 <= s && s <= 1)) + { + Vector2 P = A + r * (B - A); + + return .Intersection(P); + } + else if(MathHelper.IsZero(den)) + { + if(MathHelper.IsZero(numR)) + { + Vector2 AtoB = B - A; + Vector2 CtoD = D - C; + + float r_dot_r = Vector2.Dot(AtoB, AtoB); + + // t0 = (q − p) · r / (r · r) + float t0 = Vector2.Dot((C - A), AtoB) / r_dot_r; + + // t1 = (q + s − p) · r / (r · r) = t0 + s · r / (r · r) + float t1 = t0 + Vector2.Dot(CtoD, AtoB) / r_dot_r; + + // do interval intersection + + if(Vector2.Dot(CtoD, AtoB) < 0) + { + Swap!(t0, t1); + } + + if(IntervalIntersection(t0, t1, 0, 1) case .Ok(let intersection)) + { + return .Collinear(Line2D(A + intersection.Start * AtoB, A + intersection.End * AtoB)); + } + else + { + return .CollinearNoIntersect; + } + } + else + { + return .Parallel; + } + } + + return .None; + } + + public static void TestIntersects() + { + + { + Line2D line = .(.(0, 0), .(4, 0)); + + Line2D intersecting = .(.(2, 2), .(2, -2)); + + var result = line.Intersects(intersecting); + Vector2 intersection; + Runtime.Assert(result case .Intersection(out intersection)); + Runtime.Assert(intersection == .(2, 0)); + } + + { + Line2D line = .(.(0, -1), .(5, 2)); + + Line2D intersecting = .(.(1, 3), .(4, -2)); + + var result = line.Intersects(intersecting); + Vector2 intersection; + Runtime.Assert(result case .Intersection(out intersection)); + Runtime.Assert(intersection == .(2.5f, 0.5f)); + } + + { + Line2D line = .(.(-1, 0), .(1, 0)); + + Line2D collinear = .(.(2, 0), .( 4, 0)); + + var result = line.Intersects(collinear); + + Runtime.Assert(result case .CollinearNoIntersect); + } + + { + Line2D line = .(.(-1, 0), .(2, 0)); + + Line2D collinear = .(.(1, 0), .( 4, 0)); + + var result = line.Intersects(collinear); + + Line2D intersection; + Runtime.Assert(result case .Collinear(out intersection)); + + Runtime.Assert(intersection.Start == .(1, 0)); + Runtime.Assert(intersection.End == .(2, 0)); + } + + { + Line2D line = .(.(-1, 5), .(2, 5)); + + Line2D collinear = .(.(4, 5), .(1, 5)); + + var result = line.Intersects(collinear); + + Line2D intersection; + Runtime.Assert(result case .Collinear(out intersection)); + + Runtime.Assert(intersection.Start == .(1, 5)); + Runtime.Assert(intersection.End == .(2, 5)); + } + } + + /* + public bool Intersects(Line2D line) + { + float x1 = Start.X; + float x2 = End.X; + float x3 = line.Start.X; + float x4 = line.End.X; + + float y1 = Start.Y; + float y2 = End.Y; + float y3 = line.Start.Y; + float y4 = line.End.Y; + + float t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)); + + float u = ((x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)); + + return (0.0f <= t && t <= 1.0f && 0.0f <= u && u <= 1.0f); + } + */ + } +} \ No newline at end of file diff --git a/Sandbox/src/Obstacle.bf b/Sandbox/src/Obstacle.bf new file mode 100644 index 0000000..c1656df --- /dev/null +++ b/Sandbox/src/Obstacle.bf @@ -0,0 +1,46 @@ +using GlitchyEngine; +using GlitchyEngine.Math; +using GlitchyEngine.Renderer; +using System; + +namespace Sandbox +{ + class Obstacle + { + public Vector2 Position; + + public float HoleSize = 4.5f; + + public static float Speed = -2.0f; + + public static float ScreenWidth; + + public static Random random = new .() ~ delete _; + + public bool Dead; + + public void Update(GameTime gameTime) + { + Position.X += Speed * gameTime.DeltaTime; + + if (Position.X < -ScreenWidth - 1) + { + Dead = true; + } + } + + static Matrix mat = Matrix.Scaling(2.5f, 5, 1) * Matrix.RotationZ(MathHelper.PiOverFour); + + public void Draw() + { + Matrix matty = mat; + matty.Translation.X = Position.X; + + matty.Translation.Y = Position.Y - HoleSize; + Renderer2D.DrawQuad(matty, Color.White); + + matty.Translation.Y = Position.Y + HoleSize; + Renderer2D.DrawQuad(matty, Color.White); + } + } +} \ No newline at end of file diff --git a/Sandbox/src/Rocket.bf b/Sandbox/src/Rocket.bf new file mode 100644 index 0000000..36b2655 --- /dev/null +++ b/Sandbox/src/Rocket.bf @@ -0,0 +1,153 @@ +using GlitchyEngine.Renderer; +using GlitchyEngine.Math; +using GlitchyEngine; +using System; +using System.Collections; + +namespace Sandbox +{ + class Rocket + { + Texture2D _rocketTexture ~ _?.ReleaseRef(); + + private Vector2 _gravity = .(0, -9.81f); + + public float Power = 50; + + public float FlightSpeed = 2; + + private Vector2 _acceleration = .(0, 0); + private Vector2 _speed; + + private Vector2 _position; + + private float _rotation; + + private bool _dead; + + private bool _started = false; + + public List Obstacles; + + public int Score = 0; + + public this() + { + _rocketTexture = new Texture2D("content/RocketGame/Rocket.dds"); + _rocketTexture.SamplerState = SamplerStateManager.PointClamp; + } + + public void Update(GameTime gameTime) + { + FlightSpeed = _dead ? 0.0f : 2.0f; + + if (!_started) + { + _position = .Zero; + + if (Input.IsKeyPressed(Key.Space)) + _started = true; + else + return; + } + + UpdatePosition(gameTime); + + CheckDead(); + } + + public void Draw() + { + Renderer2D.DrawQuad(_position, .One, _rotation, _rocketTexture, .White); + + CheckObstacles(); + } + + Line2D rocketLine; + + private void UpdatePosition(GameTime gameTime) + { + _acceleration.Y = 0; + + if (!_dead && Input.IsKeyPressed(Key.Space)) + { + _acceleration.Y += Power; + } + + Vector2 newSpeed = _speed + (_acceleration + _gravity) * gameTime.DeltaTime; + + Vector2 newPosition = _position + newSpeed * gameTime.DeltaTime; + + _speed = newSpeed; + _position = newPosition; + + float ang = Math.Atan2(_speed.Y, 10) - MathHelper.PiOverTwo; + + _rotation = ang; + + Vector2 dir = Vector2.Normalize(_speed + .(10, 0)); + rocketLine = .(_position - dir / 2.8f, _position + dir / 2.4f); + } + + const float worldBorder = 2.1f; + + private void CheckDead() + { + if (Math.Abs(_position.Y) >= worldBorder) + { + _dead = true; + _speed.Y *= -0.8f; + _position.Y = Math.Clamp(_position.Y, -worldBorder, worldBorder); + } + } + + private int scoreLine = -1; + + private void CheckObstacles() + { + for (Obstacle obs in Obstacles) + { + const float f = Vector2.One.Magnitude(); + + Vector2 center = obs.Position; + + Vector2 topCenter = center + .(0, obs.HoleSize); + Vector2 topTip = topCenter - .(0, 2.5f * f); + Vector2 topRight = topCenter - .(f, 0); + Vector2 topLeft = topCenter + .(f, 0); + + Vector2 bottomCenter = center - .(0, obs.HoleSize); + Vector2 bottomTip = bottomCenter + .(0, 2.5f * f); + Vector2 bottomRight = bottomCenter - .(f, 0); + Vector2 bottomLeft = bottomCenter + .(f, 0); + + Line2D bottomR = .(bottomTip, bottomRight); + Line2D bottomL = .(bottomTip, bottomLeft); + + Line2D topR = .(topTip, topRight); + Line2D topL = .(topTip, topLeft); + + if ((bottomR.Intersects(rocketLine) case .Intersection) || + (bottomL.Intersects(rocketLine) case .Intersection) || + (topR.Intersects(rocketLine) case .Intersection) || + (topL.Intersects(rocketLine) case .Intersection)) + { + _dead = true; + } + + if (!_dead) + { + if (Line2D(bottomTip, topTip).Intersects(rocketLine) case .Intersection) + { + scoreLine = @obs.Index; + } + else if (scoreLine == @obs.Index) + { + Score++; + scoreLine = -1; + } + } + } + } + } +} \ No newline at end of file diff --git a/Sandbox/src/SandboxApp.bf b/Sandbox/src/SandboxApp.bf index c19e7bc..8e8ecbb 100644 --- a/Sandbox/src/SandboxApp.bf +++ b/Sandbox/src/SandboxApp.bf @@ -18,13 +18,14 @@ namespace Sandbox { public this() { -#if GAMMA_TEST +/*#if GAMMA_TEST PushLayer(new GammaTestLayer()); #elif SANDBOX_2D PushLayer(new ExampleLayer2D()); #else PushLayer(new ExampleLayer()); -#endif +#endif*/ + PushLayer(new GameLayer()); } [Export, LinkName("CreateApplication")]