diff --git a/DotRPG.sln b/DotRPG.sln index ac8b3c8..87d2f42 100644 --- a/DotRPG.sln +++ b/DotRPG.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotRPG.Example", "_Example\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotRPG.Objects.Complexity", "Objects\Complexity\DotRPG.Objects.Complexity.csproj", "{A5B3FC78-C694-440E-9D99-7CE4FA6A01AC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRPG.Objects.Dynamics", "Objects\Dynamics\DotRPG.Objects.Dynamics.csproj", "{35012066-AC4B-4144-90C9-7F42681652C8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {A5B3FC78-C694-440E-9D99-7CE4FA6A01AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B3FC78-C694-440E-9D99-7CE4FA6A01AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B3FC78-C694-440E-9D99-7CE4FA6A01AC}.Release|Any CPU.Build.0 = Release|Any CPU + {35012066-AC4B-4144-90C9-7F42681652C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35012066-AC4B-4144-90C9-7F42681652C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35012066-AC4B-4144-90C9-7F42681652C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35012066-AC4B-4144-90C9-7F42681652C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Objects/DotRPG.Objects.csproj b/Objects/DotRPG.Objects.csproj index 3b5929a..4e9ca0b 100644 --- a/Objects/DotRPG.Objects.csproj +++ b/Objects/DotRPG.Objects.csproj @@ -13,6 +13,14 @@ x64 + + + + + + + + diff --git a/Objects/Dynamics/DotRPG.Objects.Dynamics.csproj b/Objects/Dynamics/DotRPG.Objects.Dynamics.csproj new file mode 100644 index 0000000..60df15c --- /dev/null +++ b/Objects/Dynamics/DotRPG.Objects.Dynamics.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/Objects/Dynamics/DynamicRectObject.cs b/Objects/Dynamics/DynamicRectObject.cs new file mode 100644 index 0000000..26443e5 --- /dev/null +++ b/Objects/Dynamics/DynamicRectObject.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using DotRPG.Objects; + +namespace DotRPG.Objects.Dynamics +{ + public class DynamicRectObject + { + public Boolean Static; + public Vector2 Location; + protected Point BodySize; + public SpriteController Sprite; + public Rectangle Collider + { + get + { + return new Rectangle + ( + (int)Location.X - BodySize.X / 2, + (int)Location.Y - BodySize.Y / 2, + BodySize.X, + BodySize.Y + ); + } + } + /// + /// Vector value which describes how much body will travel (pts/s) + /// + public Vector2 Velocity; + public Single Mass; + public Vector2 AppliedForce = Vector2.Zero; + + public Single KineticEnergy + { + get + { + return Mass * (Single)Math.Pow(Velocity.Length(), 2) / 2.0f; + } + } + public Vector2 Momentum + { + get + { + return new Vector2(Velocity.X * Mass, Velocity.Y * Mass); + } + } + public DynamicRectObject(Point StartLocation, Point colliderSize, Single mass) + { + Location = StartLocation.ToVector2(); + BodySize = colliderSize; + Mass = mass; + } + + public void CollideWith(DynamicRectObject another, Boolean hitVertically) + { + if (another.Static) + { + this.Velocity = new Vector2(!hitVertically ? 0.0f : this.Velocity.X, hitVertically ? 0.0f : this.Velocity.Y); + if (hitVertically) + { + if (this.Location.Y >= another.Location.Y) + { + this.Location.Y += Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + else + { + this.Location.Y -= Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + } + else + { + if (this.Location.X >= another.Location.X) + { + this.Location.X += Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + else + { + this.Location.X -= Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + } + return; + } + Single Summary_X_Momentum = (this.Momentum.X + another.Momentum.X) / 2; + Single Summary_Y_Momentum = (this.Momentum.Y + another.Momentum.Y) / 2; + + if (hitVertically) + { + this.Velocity = new Vector2(this.Velocity.X, Summary_Y_Momentum / this.Mass); + another.Velocity = new Vector2(another.Velocity.X, Summary_Y_Momentum / another.Mass); + if (this.Mass > another.Mass) + { + if (this.Location.Y >= another.Location.Y) + { + another.Location.Y -= Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + else + { + another.Location.Y += Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + } + else + { + if (this.Location.Y >= another.Location.Y) + { + this.Location.Y += Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + else + { + this.Location.Y -= Math.Max((this.BodySize.Y / 2 + another.BodySize.Y / 2) - Math.Abs(this.Location.Y - another.Location.Y), 0); + } + } + } + else + { + this.Velocity = new Vector2(Summary_X_Momentum / this.Mass, this.Velocity.Y); + another.Velocity = new Vector2(Summary_X_Momentum / another.Mass, another.Velocity.Y); + if (this.Mass > another.Mass) + { + if (this.Location.X >= another.Location.X) + { + another.Location.X -= Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + else + { + another.Location.X += Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + } + else + { + if (this.Location.X >= another.Location.X) + { + this.Location.X += Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + else + { + this.Location.X -= Math.Max((this.BodySize.X / 2 + another.BodySize.X / 2) - Math.Abs(this.Location.X - another.Location.X), 0); + } + } + } + } + + public void Draw(SpriteBatch _sb, GameTime gameTime, Int32 VirtualVSize, Point scrollOffset, Point scrollSize) + { + Single sizeMorph = 1.0f * scrollSize.Y / VirtualVSize; + Vector2 location = new Vector2 + ( + Location.X * sizeMorph - (BodySize.X * sizeMorph / 2) - scrollOffset.X * sizeMorph, + Location.Y * sizeMorph - (BodySize.Y * sizeMorph / 2) - scrollOffset.Y * sizeMorph + ); + Sprite.Draw(_sb, location, gameTime, sizeMorph); + } + + public Boolean TryCollideWith(DynamicRectObject another) + { + if (this.Collider.Intersects(another.Collider)) + { + if (1.0 * Math.Abs(this.Location.X - another.Location.X) / (this.BodySize.X / 2 + another.BodySize.X / 2) >= 1.0 * Math.Abs(this.Location.Y - another.Location.Y) / (this.BodySize.Y / 2 + another.BodySize.Y / 2)) + { + CollideWith(another, false); + } + else + { + CollideWith(another, true); + } + return true; + } + return false; + } + + public void Update(GameTime gameTime) + { + Velocity += AppliedForce / ((Single)gameTime.ElapsedGameTime.TotalSeconds * this.Mass); + Location += Velocity / (Single)gameTime.ElapsedGameTime.TotalSeconds; + AppliedForce = Vector2.Zero; + } + + public void FullStop() + { + Velocity = Vector2.Zero; + } + } +} diff --git a/Objects/SpriteController.cs b/Objects/SpriteController.cs index 7263371..4ad63b1 100644 --- a/Objects/SpriteController.cs +++ b/Objects/SpriteController.cs @@ -55,7 +55,7 @@ public void AddAnimationSequence(String sequenceName, Texture2D frames, UInt16 f LoopTo.Add(sequenceName, loopTo); } - public void Draw(SpriteBatch _sb, Vector2 drawLocation, GameTime gameTime) + public void Draw(SpriteBatch _sb, Vector2 drawLocation, GameTime gameTime, Single drawSize = 1.0f) { Single addFrames = 0.0f; if (PlaybackSpeed > 0.0f) @@ -88,7 +88,7 @@ public void Draw(SpriteBatch _sb, Vector2 drawLocation, GameTime gameTime) Color.White, 0, Vector2.Zero, - 1, + drawSize, SpriteEffects.None, 0 ); diff --git a/_Example/DotRPG.Example.csproj b/_Example/DotRPG.Example.csproj index aef7878..c937658 100644 --- a/_Example/DotRPG.Example.csproj +++ b/_Example/DotRPG.Example.csproj @@ -1,4 +1,4 @@ - + WinExe netcoreapp3.1 @@ -30,5 +30,6 @@ + \ No newline at end of file diff --git a/_Example/DynamicsTestFrame.cs b/_Example/DynamicsTestFrame.cs new file mode 100644 index 0000000..c0760d7 --- /dev/null +++ b/_Example/DynamicsTestFrame.cs @@ -0,0 +1,169 @@ +// #define MUTE +using System; +using System.Collections.Generic; +using DotRPG.Objects; +using DotRPG.Objects.Dynamics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; +using Microsoft.Xna.Framework.Graphics; + +namespace DotRPG._Example +{ + class DynamicsTestFrame : Frame + { + DynamicRectObject Player; + DynamicRectObject Obstacle1; + DynamicRectObject Obstacle2; + SoundEffectInstance Sliding; + + public override int FrameID + { + get + { + return 1; + } + } + + public DynamicsTestFrame(Game owner, ResourceHeap rh, HashSet eventSet) : base(owner, rh, eventSet) + { + + } + + public override void LoadContent() + { + FrameResources.Textures.Add("cube-p", Owner.Content.Load("Texture2D/cube-p")); + Player.Sprite = new SpriteController(1000 / 60.0f, FrameResources.Textures["cube-p"]); + FrameResources.Textures.Add("cube-o", Owner.Content.Load("Texture2D/cube-o")); + FrameResources.Textures.Add("cube-o2", Owner.Content.Load("Texture2D/cube-o2")); + Obstacle1.Sprite = new SpriteController(1000 / 60.0f, FrameResources.Textures["cube-o"]); + Obstacle2.Sprite = new SpriteController(1000 / 60.0f, FrameResources.Textures["cube-o2"]); + FrameResources.Sounds.Add("slide", Owner.Content.Load("Sounds/wallcling")); + FrameResources.Sounds.Add("impact", Owner.Content.Load("Sounds/impact")); + Sliding = FrameResources.Sounds["slide"].CreateInstance(); + Sliding.IsLooped = true; + } + + public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle drawZone) + { + Player.Draw(spriteBatch, gameTime, 540, Point.Zero, new Point(drawZone.Width, drawZone.Height)); + Obstacle1.Draw(spriteBatch, gameTime, 540, Point.Zero, new Point(drawZone.Width, drawZone.Height)); + Obstacle2.Draw(spriteBatch, gameTime, 540, Point.Zero, new Point(drawZone.Width, drawZone.Height)); + spriteBatch.DrawString(FrameResources.Global.Fonts["vcr"], String.Format("Player: ({0}, {1}), Velocity=({2}, {3})", Player.Collider.X, Player.Collider.Y, Player.Velocity.X, Player.Velocity.Y), new Vector2(0, 12), Color.White); + } + + public override void Initialize() + { + Player = new DynamicRectObject(new Point(48, 48), new Point(32, 32), 20.0f); + Obstacle1 = new DynamicRectObject(new Point(128, 128), new Point(32, 32), 10.0f); + Obstacle2 = new DynamicRectObject(new Point(192, 128), new Point(64, 64), 15.0f); + } + + public override void Update(GameTime gameTime, bool[] controls) + { + Single loco_x = 0.0f; Single loco_y = 0.0f; + if (controls[0]) { loco_y -= 1.0f; } + if (controls[1]) { loco_y += 1.0f; } + if (controls[2]) { loco_x -= 1.0f; } + if (controls[3]) { loco_x += 1.0f; } + Vector2 Locomotion = new Vector2(loco_x, loco_y); + Locomotion /= (Locomotion.Length() != 0 ? Locomotion.Length() : 1.0f); + Locomotion *= 0.1f; + Vector2 FrictionVector1 = new Vector2(0.0f - (Obstacle1.Velocity.X / (Obstacle1.Velocity.Length() != 0 ? Obstacle1.Velocity.Length() : 1.0f)), 0.0f - (Obstacle1.Velocity.Y / (Obstacle1.Velocity.Length() != 0 ? Obstacle1.Velocity.Length() : 1.0f))); + FrictionVector1 *= Obstacle1.Mass * 0.00001f; + Vector2 FrictionVector2 = new Vector2(0.0f - (Obstacle2.Velocity.X / (Obstacle2.Velocity.Length() != 0 ? Obstacle2.Velocity.Length() : 1.0f)), 0.0f - (Obstacle2.Velocity.Y / (Obstacle2.Velocity.Length() != 0 ? Obstacle2.Velocity.Length() : 1.0f))); + FrictionVector2 *= Obstacle2.Mass * 0.00000005f; + if (Obstacle1.Velocity.Length() >= 0.001f) + { + #if !MUTE + Sliding.Play(); + #endif + } + else + { + Sliding.Stop(); + } + Obstacle1.AppliedForce += FrictionVector1; + Obstacle2.AppliedForce += FrictionVector2; + if (Obstacle1.Collider.Y <= 0 || Obstacle1.Collider.Y >= 540) + { + Obstacle1.Location = new Vector2(Obstacle1.Location.X, (Obstacle1.Collider.Y <= 0 ? 16.0f : 524.0f)); + Obstacle1.Velocity = new Vector2(Obstacle1.Velocity.X, 0 - Obstacle1.Velocity.Y); + #if !MUTE + FrameResources.Sounds["impact"].Play(); + #endif + } + if (Obstacle1.Collider.X <= 0 || Obstacle1.Collider.X >= 960) + { + Obstacle1.Location = new Vector2((Obstacle1.Collider.X <= 0 ? 16.0f : 944.0f), Obstacle1.Location.Y); + Obstacle1.Velocity = new Vector2(0 - Obstacle1.Velocity.X, Obstacle1.Velocity.Y); + #if !MUTE + FrameResources.Sounds["impact"].Play(); + #endif + } + if (Obstacle2.Collider.Y <= 0 || Obstacle2.Collider.Y >= 540) + { + Obstacle2.Location = new Vector2(Obstacle2.Location.X, (Obstacle2.Collider.Y <= 0 ? 32.0f : 508.0f)); + Obstacle2.Velocity = new Vector2(Obstacle2.Velocity.X, 0 - Obstacle2.Velocity.Y); + #if !MUTE + // FrameResources.Sounds["impact"].Play(); + #endif + } + if (Obstacle2.Collider.X <= 0 || Obstacle2.Collider.X >= 960) + { + Obstacle2.Location = new Vector2((Obstacle2.Collider.X <= 0 ? 32.0f : 928.0f), Obstacle2.Location.Y); + Obstacle2.Velocity = new Vector2(0 - Obstacle2.Velocity.X, Obstacle2.Velocity.Y); + #if !MUTE + // FrameResources.Sounds["impact"].Play(); + #endif + } + if (Player.TryCollideWith(Obstacle1) && Math.Max(Player.Momentum.Length(), Obstacle1.Momentum.Length()) > 1.5f) + { + Player.FullStop(); + #if !MUTE + FrameResources.Sounds["impact"].Play(); + #endif + } + if (Player.TryCollideWith(Obstacle2)) + { + Player.FullStop(); + #if !MUTE + // FrameResources.Sounds["impact"].Play(); + #endif + } + if (Obstacle1.TryCollideWith(Obstacle2) && Math.Max(Obstacle1.Momentum.Length(), Obstacle2.Momentum.Length()) > 1.5f) + { + #if !MUTE + FrameResources.Sounds["impact"].Play(); + #endif + } + Player.Velocity = Locomotion; + Player.Update(gameTime); + Obstacle1.Update(gameTime); + if (Player.Collider.Y <= 0 || Player.Collider.Y >= 540) + { + Player.Location = new Vector2(Player.Location.X, (Player.Collider.Y <= 0 ? 16.0f : 524.0f)); + Player.FullStop(); + } + if (Player.Collider.X <= 0 || Player.Collider.X >= 960) + { + Player.Location = new Vector2((Player.Collider.X <= 0 ? 16.0f : 944.0f), Player.Location.Y); + Player.FullStop(); + } + base.Update(gameTime, controls); + } + + public override void SetPlayerPosition(object sender, EventArgs e, GameTime gameTime) + { + throw new NotImplementedException(); + } + + public override void UnloadContent() + { + FrameResources.Dispose(); + Player = null; + Obstacle1 = null; + Obstacle2 = null; + Sliding = null; + } + } +} diff --git a/_Example/Game1.cs b/_Example/Game1.cs index 67f9d30..afaf11d 100644 --- a/_Example/Game1.cs +++ b/_Example/Game1.cs @@ -1,4 +1,5 @@ // #define FORCE_4x3 +// #define MUTE using System; using System.Collections.Generic; using Microsoft.Xna.Framework; @@ -67,7 +68,7 @@ public static void SetFrameNumber(Object sender, EventArgs e, GameTime gameTime) private void StartScroll(Object sender, EventArgs e, GameTime gameTime) { - ActiveFrame = Frames[0]; + ActiveFrame = Frames[1]; ActiveFrame.LoadContent(); } @@ -83,6 +84,8 @@ protected override void Initialize() ResetAspectRatio(); Frames.Add(new DemoFrame(this, ResourceHGlobal, LogicEventSet)); Frames[0].Initialize(); + Frames.Add(new DynamicsTestFrame(this, ResourceHGlobal, LogicEventSet)); + Frames[1].Initialize(); base.Initialize(); } diff --git a/_Example/GameData/DotRPG.Example_data.mgcb b/_Example/GameData/DotRPG.Example_data.mgcb index ca37352..3e75ae6 100644 --- a/_Example/GameData/DotRPG.Example_data.mgcb +++ b/_Example/GameData/DotRPG.Example_data.mgcb @@ -33,6 +33,12 @@ /processorParam:Quality=Best /build:Sounds/clickText.wav +#begin Sounds/impact.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sounds/impact.wav + #begin Sounds/pixelText.wav /importer:WavImporter /processor:SoundEffectProcessor @@ -45,6 +51,12 @@ /processorParam:Quality=Best /build:Sounds/text-scroll.wav +#begin Sounds/wallcling.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sounds/wallcling.wav + #begin Texture2D/Banana.png /importer:TextureImporter /processor:TextureProcessor @@ -57,6 +69,42 @@ /processorParam:TextureFormat=Color /build:Texture2D/Banana.png +#begin Texture2D/cube-o.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Texture2D/cube-o.png + +#begin Texture2D/cube-o2.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Texture2D/cube-o2.png + +#begin Texture2D/cube-p.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Texture2D/cube-p.png + #begin Texture2D/ScrollMarker.png /importer:TextureImporter /processor:TextureProcessor diff --git a/_Example/GameData/Sounds/impact.wav b/_Example/GameData/Sounds/impact.wav new file mode 100644 index 0000000..3a5792e Binary files /dev/null and b/_Example/GameData/Sounds/impact.wav differ diff --git a/_Example/GameData/Sounds/wallcling.wav b/_Example/GameData/Sounds/wallcling.wav new file mode 100644 index 0000000..0e0c696 Binary files /dev/null and b/_Example/GameData/Sounds/wallcling.wav differ diff --git a/_Example/GameData/Texture2D/cube-o.png b/_Example/GameData/Texture2D/cube-o.png new file mode 100644 index 0000000..0267153 Binary files /dev/null and b/_Example/GameData/Texture2D/cube-o.png differ diff --git a/_Example/GameData/Texture2D/cube-o2.png b/_Example/GameData/Texture2D/cube-o2.png new file mode 100644 index 0000000..1eae1e7 Binary files /dev/null and b/_Example/GameData/Texture2D/cube-o2.png differ diff --git a/_Example/GameData/Texture2D/cube-p.png b/_Example/GameData/Texture2D/cube-p.png new file mode 100644 index 0000000..d02435b Binary files /dev/null and b/_Example/GameData/Texture2D/cube-p.png differ