Add project files.
This commit is contained in:
parent
fdb819f7dd
commit
01b3ce4360
26
Hook Climber.csproj
Normal file
26
Hook Climber.csproj
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<RootNamespace>Hook_Climber</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Raylib-cs" Version="6.0.0" />
|
||||||
|
<PackageReference Include="TiledCS" Version="3.3.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="main.tmx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="main.tsx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
25
Hook Climber.sln
Normal file
25
Hook Climber.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.7.34221.43
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hook Climber", "Hook Climber.csproj", "{47ACEAFD-7819-4DAB-BAA2-7B98C7132276}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{47ACEAFD-7819-4DAB-BAA2-7B98C7132276}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{47ACEAFD-7819-4DAB-BAA2-7B98C7132276}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{47ACEAFD-7819-4DAB-BAA2-7B98C7132276}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{47ACEAFD-7819-4DAB-BAA2-7B98C7132276}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {B80AB0B0-39BA-44FF-A87E-F21DF586CC1C}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
463
Program.cs
Normal file
463
Program.cs
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
using Raylib_cs;
|
||||||
|
using static Raylib_cs.Raymath;
|
||||||
|
using System.Numerics;
|
||||||
|
using TiledCS;
|
||||||
|
|
||||||
|
namespace Hook_Climber;
|
||||||
|
|
||||||
|
static class Utils
|
||||||
|
{
|
||||||
|
public static Rectangle centeredRect(Vector2 center, Vector2 size)
|
||||||
|
{
|
||||||
|
return new Rectangle(center - size / 2, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
public static float playerMaxSpeed = 650.0f;
|
||||||
|
public static float playerWalkForce = 4500.0f;
|
||||||
|
public static float playerFriction = 0.999f;
|
||||||
|
public static float playerGravity = 2000.0f;
|
||||||
|
|
||||||
|
public static float hookDistance = 75f;
|
||||||
|
public static float hookPower = 300f;
|
||||||
|
public static float hookGravity = 600.0f;
|
||||||
|
public static float hookOffset = 50f;
|
||||||
|
public static float hookRopeStrength = 30f;
|
||||||
|
|
||||||
|
|
||||||
|
public static Vector2 hookSize = new Vector2(10, 10);
|
||||||
|
public static Vector2 playerSize = new Vector2(50, 50);
|
||||||
|
|
||||||
|
public Player player;
|
||||||
|
public List<Rectangle> platforms;
|
||||||
|
|
||||||
|
public static Rectangle GetSweptBroadphaseBox(Rectangle b, Vector2 v)
|
||||||
|
{
|
||||||
|
Rectangle broadphasebox = b;
|
||||||
|
|
||||||
|
broadphasebox.X = v.X > 0 ? b.X : b.X + v.X;
|
||||||
|
broadphasebox.Y = v.Y > 0 ? b.Y : b.Y + v.Y;
|
||||||
|
broadphasebox.Width = v.X > 0 ? v.X + b.Width : b.Width - v.X;
|
||||||
|
broadphasebox.Height = v.Y > 0 ? v.Y + b.Height : b.Height - v.Y;
|
||||||
|
|
||||||
|
return broadphasebox;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<(Rectangle, float, Vector2)> IterCollisions(Rectangle collider, Vector2 velocity, float dt)
|
||||||
|
{
|
||||||
|
foreach (var platform in platforms)
|
||||||
|
{
|
||||||
|
Vector2 normal;
|
||||||
|
float collisiontime = CheckCollision(
|
||||||
|
collider,
|
||||||
|
velocity * dt,
|
||||||
|
platform,
|
||||||
|
out normal
|
||||||
|
);
|
||||||
|
if (normal.X == 0 && normal.Y == 0) continue;
|
||||||
|
|
||||||
|
yield return (platform, collisiontime * dt, normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float CheckCollision(Rectangle b1, Vector2 v1, Rectangle b2, out Vector2 normal)
|
||||||
|
{
|
||||||
|
normal.X = 0;
|
||||||
|
normal.Y = 0;
|
||||||
|
if (!CheckCollisionRecs(GetSweptBroadphaseBox(b1, v1), b2))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float entryX, exitX;
|
||||||
|
if (v1.X > 0)
|
||||||
|
{
|
||||||
|
entryX = b2.X - (b1.X + b1.Width);
|
||||||
|
exitX = (b2.X + b2.Width) - b1.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entryX = (b2.X + b2.Width) - b1.X;
|
||||||
|
exitX = b2.X - (b1.X + b1.Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
float entryY, exitY;
|
||||||
|
if (v1.Y > 0)
|
||||||
|
{
|
||||||
|
entryY = b2.Y - (b1.Y + b1.Height);
|
||||||
|
exitY = (b2.Y + b2.Height) - b1.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entryY = (b2.Y + b2.Height) - b1.Y;
|
||||||
|
exitY = b2.Y - (b1.Y + b1.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
float entryXTime, exitXTime;
|
||||||
|
if (v1.X == 0)
|
||||||
|
{
|
||||||
|
entryXTime = float.MinValue;
|
||||||
|
exitXTime = float.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entryXTime = entryX / v1.X;
|
||||||
|
exitXTime = exitX / v1.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
float entryYTime, exitYTime;
|
||||||
|
if (v1.Y == 0)
|
||||||
|
{
|
||||||
|
entryYTime = float.MinValue;
|
||||||
|
exitYTime = float.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entryYTime = entryY / v1.Y;
|
||||||
|
exitYTime = exitY / v1.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float entryTime = Math.Max(entryXTime, entryYTime);
|
||||||
|
float exitTime = Math.Min(exitXTime, exitYTime);
|
||||||
|
|
||||||
|
if (entryTime > exitTime || entryXTime < 0 && entryYTime < 0 || entryXTime > 1.0f || entryYTime > 1.0f)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entryXTime > entryYTime)
|
||||||
|
{
|
||||||
|
normal.X = entryX < 0.0 ? 1 : -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
normal.Y = entryY < 0.0 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Hook
|
||||||
|
{
|
||||||
|
public Game game;
|
||||||
|
|
||||||
|
public Vector2 offset;
|
||||||
|
public Vector2 dir;
|
||||||
|
|
||||||
|
public Vector2 primedAt;
|
||||||
|
public bool shown;
|
||||||
|
public bool primed;
|
||||||
|
|
||||||
|
public bool headReleased;
|
||||||
|
public Vector2 headPosition;
|
||||||
|
public Vector2 headVelocity;
|
||||||
|
public bool headGrounded;
|
||||||
|
public float reelDir = 0;
|
||||||
|
public float idealRopeLength = 200f;
|
||||||
|
|
||||||
|
public GamepadAxis moveX;
|
||||||
|
public GamepadAxis moveY;
|
||||||
|
public GamepadButton use;
|
||||||
|
public GamepadButton prime;
|
||||||
|
|
||||||
|
public void resetHead()
|
||||||
|
{
|
||||||
|
headGrounded = false;
|
||||||
|
headReleased = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Vector2> headProjection(uint steps, float dt)
|
||||||
|
{
|
||||||
|
var points = new List<Vector2>();
|
||||||
|
|
||||||
|
var position = headPosition;
|
||||||
|
var velocity = headVelocity;
|
||||||
|
for (int i = 0; i < steps; i++)
|
||||||
|
{
|
||||||
|
if (updatePhysics(ref position, ref velocity, dt)) break;
|
||||||
|
points.Add(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool updatePhysics(ref Vector2 position, ref Vector2 velocity, float dt)
|
||||||
|
{
|
||||||
|
velocity.Y += Game.hookGravity * dt;
|
||||||
|
bool collisionOccured = checkAndResolveCollisions(Utils.centeredRect(position, Game.hookSize), ref velocity, dt);
|
||||||
|
position += velocity * dt;
|
||||||
|
|
||||||
|
return collisionOccured;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool checkAndResolveCollisions(Rectangle collider, ref Vector2 velocity, float dt)
|
||||||
|
{
|
||||||
|
bool collision = false;
|
||||||
|
|
||||||
|
foreach (var (_, collisiontime, normal) in game.IterCollisions(collider, velocity, dt))
|
||||||
|
{
|
||||||
|
velocity.X *= 0;
|
||||||
|
velocity.Y *= 0;
|
||||||
|
|
||||||
|
//velocity.X = velocity.X * collisiontime / dt;
|
||||||
|
//velocity.Y = velocity.Y * collisiontime / dt;
|
||||||
|
collision = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collision;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Player
|
||||||
|
{
|
||||||
|
public Vector2 position = new Vector2(0, 0);
|
||||||
|
public Vector2 velocity = new Vector2(0, 0);
|
||||||
|
|
||||||
|
public Rectangle collisionRect()
|
||||||
|
{
|
||||||
|
return new Rectangle(position - Game.playerSize / 2, Game.playerSize.X, Game.playerSize.Y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
public static void Main()
|
||||||
|
{
|
||||||
|
InitWindow(2000, 1200, "Hook Climber");
|
||||||
|
SetWindowState(ConfigFlags.ResizableWindow);
|
||||||
|
|
||||||
|
var map = new TiledMap("./main.tmx");
|
||||||
|
var tilesets = map.GetTiledTilesets("./");
|
||||||
|
|
||||||
|
var game = new Game();
|
||||||
|
game.player = new Player();
|
||||||
|
|
||||||
|
var leftHook = new Hook
|
||||||
|
{
|
||||||
|
game = game,
|
||||||
|
offset = new Vector2(-Game.hookOffset, 0),
|
||||||
|
moveX = GamepadAxis.LeftX,
|
||||||
|
moveY = GamepadAxis.LeftY,
|
||||||
|
use = GamepadButton.LeftTrigger2,
|
||||||
|
prime = GamepadButton.LeftTrigger1,
|
||||||
|
idealRopeLength = 200
|
||||||
|
};
|
||||||
|
|
||||||
|
var rightHook = new Hook
|
||||||
|
{
|
||||||
|
game = game,
|
||||||
|
offset = new Vector2(Game.hookOffset, 0),
|
||||||
|
moveX = GamepadAxis.RightX,
|
||||||
|
moveY = GamepadAxis.RightY,
|
||||||
|
use = GamepadButton.RightTrigger2,
|
||||||
|
prime = GamepadButton.RightTrigger1,
|
||||||
|
idealRopeLength = 200
|
||||||
|
};
|
||||||
|
|
||||||
|
var hooks = new Hook[]{ leftHook, rightHook };
|
||||||
|
|
||||||
|
var gamepadId = 0;
|
||||||
|
|
||||||
|
game.platforms = new List<Rectangle> {
|
||||||
|
new Rectangle(-450, game.player.position.Y + Game.playerSize.Y/2, 1000, 20),
|
||||||
|
new Rectangle(0, -200, 100, 10),
|
||||||
|
new Rectangle(-100, -400, 100, 10),
|
||||||
|
new Rectangle(100, -600, 100, 10),
|
||||||
|
new Rectangle(-200, -800, 100, 10),
|
||||||
|
new Rectangle(-300, -1000, 100, 10),
|
||||||
|
};
|
||||||
|
|
||||||
|
var player = game.player;
|
||||||
|
|
||||||
|
var camera = new Camera2D();
|
||||||
|
camera.Target = player.position;
|
||||||
|
|
||||||
|
while (!WindowShouldClose())
|
||||||
|
{
|
||||||
|
var dt = GetFrameTime();
|
||||||
|
var windowWidth = GetScreenWidth();
|
||||||
|
var windowHeight = GetScreenHeight();
|
||||||
|
|
||||||
|
camera.Offset = new Vector2(windowWidth / 2, windowHeight / 2);
|
||||||
|
camera.Target = Vector2.Lerp(camera.Target, player.position, 20 * dt);
|
||||||
|
camera.Zoom = 1;
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(Color.White);
|
||||||
|
BeginMode2D(camera);
|
||||||
|
|
||||||
|
var dx = 0f;
|
||||||
|
if (IsKeyDown(KeyboardKey.D))
|
||||||
|
{
|
||||||
|
dx += 1;
|
||||||
|
}
|
||||||
|
if (IsKeyDown(KeyboardKey.A))
|
||||||
|
{
|
||||||
|
dx -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsGamepadAvailable(gamepadId))
|
||||||
|
{
|
||||||
|
foreach (var hook in hooks)
|
||||||
|
{
|
||||||
|
if (IsGamepadButtonPressed(gamepadId, hook.prime))
|
||||||
|
{
|
||||||
|
hook.primedAt = hook.dir;
|
||||||
|
}
|
||||||
|
if (IsGamepadButtonDown(gamepadId, hook.prime))
|
||||||
|
{
|
||||||
|
var trajectory = hook.primedAt - hook.dir;
|
||||||
|
|
||||||
|
hook.headPosition = player.position + hook.primedAt * Game.hookDistance + hook.offset;
|
||||||
|
hook.headVelocity = trajectory * Game.hookPower + player.velocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsGamepadButtonReleased(gamepadId, hook.prime))
|
||||||
|
{
|
||||||
|
if (hook.headReleased && (hook.primedAt - hook.dir).Length() < 0.01)
|
||||||
|
{
|
||||||
|
hook.resetHead();
|
||||||
|
}
|
||||||
|
else if (hook.primed && hook.shown)
|
||||||
|
{
|
||||||
|
hook.headReleased = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hook.shown = IsGamepadButtonDown(gamepadId, hook.use);
|
||||||
|
hook.primed = IsGamepadButtonDown(gamepadId, hook.prime);
|
||||||
|
|
||||||
|
if (!hook.headReleased)
|
||||||
|
{
|
||||||
|
hook.dir.X = GetGamepadAxisMovement(gamepadId, hook.moveX);
|
||||||
|
hook.dir.Y = GetGamepadAxisMovement(gamepadId, hook.moveY);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
hook.dir = new Vector2();
|
||||||
|
|
||||||
|
if (IsGamepadButtonDown(gamepadId, hook.use))
|
||||||
|
{
|
||||||
|
hook.reelDir = GetGamepadAxisMovement(gamepadId, hook.moveY);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
hook.reelDir = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dx += GetGamepadAxisMovement(gamepadId, GamepadAxis.LeftX);
|
||||||
|
}
|
||||||
|
|
||||||
|
dx = Clamp(dx, -1, 1);
|
||||||
|
|
||||||
|
if (leftHook.shown)
|
||||||
|
{
|
||||||
|
dx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.velocity.X += dx * Game.playerWalkForce * dt;
|
||||||
|
player.velocity.Y += Game.playerGravity * dt;
|
||||||
|
player.velocity = Vector2ClampValue(player.velocity, 0, Game.playerMaxSpeed);
|
||||||
|
player.velocity *= (float)Math.Pow(1 - Game.playerFriction, dt);
|
||||||
|
|
||||||
|
foreach (var (_, collisiontime, normal) in game.IterCollisions(player.collisionRect(), player.velocity, dt))
|
||||||
|
{
|
||||||
|
float remainingtime = (1.0f - collisiontime);
|
||||||
|
float dotprod = (player.velocity.X * normal.Y + player.velocity.Y * normal.X) * remainingtime;
|
||||||
|
// player.velocity.X = dotprod * normal.Y;
|
||||||
|
player.velocity.Y = dotprod * normal.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.position += player.velocity * dt;
|
||||||
|
|
||||||
|
DrawCircle(0, 0, 5, Color.Red);
|
||||||
|
DrawRectangleRec(player.collisionRect(), Color.Black);
|
||||||
|
|
||||||
|
foreach (var hook in hooks)
|
||||||
|
{
|
||||||
|
|
||||||
|
var handPosition = player.position + hook.dir * Game.hookDistance + hook.offset;
|
||||||
|
|
||||||
|
if (hook.headReleased)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!hook.headGrounded)
|
||||||
|
{
|
||||||
|
hook.headGrounded = hook.updatePhysics(ref hook.headPosition, ref hook.headVelocity, dt);
|
||||||
|
|
||||||
|
if (hook.headGrounded)
|
||||||
|
{
|
||||||
|
hook.idealRopeLength = (handPosition - hook.headPosition).Length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hook.headGrounded)
|
||||||
|
{
|
||||||
|
hook.idealRopeLength += hook.reelDir * dt * 150;
|
||||||
|
hook.idealRopeLength = Math.Max(hook.idealRopeLength, 0f);
|
||||||
|
|
||||||
|
var handToHead = hook.headPosition - handPosition;
|
||||||
|
player.velocity += (handToHead.Length() - hook.idealRopeLength) * Vector2Normalize(handToHead) * Game.hookRopeStrength * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawRectangleRec(Utils.centeredRect(hook.headPosition, Game.hookSize), Color.DarkBlue);
|
||||||
|
DrawLineV(handPosition, hook.headPosition, Color.Blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hook.shown && !hook.headReleased) continue;
|
||||||
|
|
||||||
|
if (hook.primed)
|
||||||
|
{
|
||||||
|
var primePosition = player.position + hook.primedAt * Game.hookDistance + hook.offset;
|
||||||
|
DrawRectangleRec(Utils.centeredRect(primePosition, Game.hookSize), Color.DarkGray);
|
||||||
|
DrawLineV(handPosition, primePosition, Color.Gray);
|
||||||
|
|
||||||
|
var projection = hook.headProjection(100, 1f / 60f);
|
||||||
|
if (projection.Count > 0)
|
||||||
|
{
|
||||||
|
var projectionArray = projection.ToArray();
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (Vector2* pointerToFirst = &projectionArray[0])
|
||||||
|
{
|
||||||
|
DrawLineStrip(pointerToFirst, projectionArray.Length, Color.Blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawRectangleRec(Utils.centeredRect(handPosition, Game.hookSize), Color.Blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var platform in game.platforms)
|
||||||
|
{
|
||||||
|
DrawRectangleRec(platform, Color.Gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndMode2D();
|
||||||
|
|
||||||
|
{ // Speed bar
|
||||||
|
var speedWidth = 300.0f;
|
||||||
|
DrawText("Speed", 10, 10, 20, Color.Black);
|
||||||
|
DrawRectangleRec(new Rectangle(75, 10, speedWidth, 20), Color.Gray);
|
||||||
|
DrawRectangleRec(new Rectangle(75, 10, speedWidth * (player.velocity.Length() / Game.playerMaxSpeed), 20), Color.Red);
|
||||||
|
DrawText($"{player.velocity.X:N3}", 400, 10, 20, Color.Black);
|
||||||
|
DrawText($"{player.velocity.Y:N3}", 500, 10, 20, Color.Black);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Rope lengths
|
||||||
|
DrawText($"Left ideal rope length: {leftHook.idealRopeLength}", 10, 30, 20, Color.Black);
|
||||||
|
DrawText($"Right ideal rope length: {rightHook.idealRopeLength}", 10, 50, 20, Color.Black);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user