Projektdateien hinzufügen.
This commit is contained in:
286
World.cs
Normal file
286
World.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
using Triangle = osu.Framework.Graphics.Shapes.Triangle;
|
||||
|
||||
namespace HillLike;
|
||||
|
||||
public class World : CompositeDrawable
|
||||
{
|
||||
private readonly Camera _camera = new();
|
||||
private readonly CarDrawable _carDrawable;
|
||||
private readonly TerrainDrawable _terrainDrawable;
|
||||
|
||||
private readonly Container _worldLayer;
|
||||
public readonly SimpleCar Car = new();
|
||||
|
||||
public World()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren =
|
||||
[
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(150, 210, 255, 255)
|
||||
},
|
||||
_worldLayer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children =
|
||||
[
|
||||
_terrainDrawable = new TerrainDrawable(),
|
||||
_carDrawable = new CarDrawable(Car)
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public sealed override Axes RelativeSizeAxes
|
||||
{
|
||||
get => base.RelativeSizeAxes;
|
||||
set => base.RelativeSizeAxes = value;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Car.Reset(new Vector2(0, Terrain.HeightAt(0) + 3f));
|
||||
_camera.Reset(Car.Position);
|
||||
}
|
||||
|
||||
public void Step(float dt)
|
||||
{
|
||||
dt = Math.Clamp(dt, 0, 1 / 20f);
|
||||
Car.Step(dt);
|
||||
_camera.Follow(dt, Car.Position);
|
||||
|
||||
var pixelsPerUnit = _camera.PixelsPerUnit;
|
||||
var viewCenter = DrawSize / 2;
|
||||
|
||||
_terrainDrawable.SetCamera(_camera, viewCenter);
|
||||
_carDrawable.SetCamera(_camera, viewCenter);
|
||||
}
|
||||
|
||||
public void HandleKeyDown(KeyDownEvent e)
|
||||
{
|
||||
Car.HandleKeyDown(e);
|
||||
if (e.Key is Key.R)
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void HandleKeyUp(KeyUpEvent e)
|
||||
{
|
||||
Car.HandleKeyUp(e);
|
||||
}
|
||||
|
||||
|
||||
private class Camera
|
||||
{
|
||||
public readonly float PixelsPerUnit = 60f;
|
||||
public Vector2 Position;
|
||||
|
||||
public void Reset(Vector2 pos)
|
||||
{
|
||||
Position = pos;
|
||||
}
|
||||
|
||||
public void Follow(float dt, Vector2 target)
|
||||
{
|
||||
var desired = target + new Vector2(5f, 2f);
|
||||
var smooth = 1f - MathF.Exp(-dt * 4.5f);
|
||||
Position = Vector2.Lerp(Position, desired, smooth);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class WorldDrawable : CompositeDrawable
|
||||
{
|
||||
protected Camera? Camera;
|
||||
protected Vector2 ViewCenter;
|
||||
|
||||
public void SetCamera(Camera cam, Vector2 viewCenter)
|
||||
{
|
||||
Camera = cam;
|
||||
ViewCenter = viewCenter;
|
||||
UpdateFromWorld();
|
||||
}
|
||||
|
||||
protected Vector2 ToScreen(Vector2 worldPos)
|
||||
{
|
||||
var rel = worldPos - Camera.Position;
|
||||
return new Vector2(
|
||||
ViewCenter.X + rel.X * Camera.PixelsPerUnit,
|
||||
ViewCenter.Y - rel.Y * Camera.PixelsPerUnit
|
||||
);
|
||||
}
|
||||
|
||||
protected float ToScreen(float worldLen)
|
||||
{
|
||||
return worldLen * Camera.PixelsPerUnit;
|
||||
}
|
||||
|
||||
protected abstract void UpdateFromWorld();
|
||||
}
|
||||
|
||||
private class TerrainDrawable : WorldDrawable
|
||||
{
|
||||
private readonly Container _segments = new() { RelativeSizeAxes = Axes.Both };
|
||||
public TerrainDrawable()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
AddInternal(_segments);
|
||||
}
|
||||
|
||||
protected override void UpdateFromWorld()
|
||||
{
|
||||
if (Camera is null) return;
|
||||
|
||||
_segments.Clear();
|
||||
|
||||
var halfWidthWorld = DrawWidth / Camera.PixelsPerUnit;
|
||||
var left = Camera.Position.X - halfWidthWorld - 2f;
|
||||
var right = Camera.Position.X + halfWidthWorld + 2f;
|
||||
|
||||
var step = 0.6f;
|
||||
var count = (int)Math.Ceiling((right - left) / step);
|
||||
|
||||
for (var i = 0; i < count; i += 1)
|
||||
{
|
||||
var x0 = left + i * step;
|
||||
var x1 = x0 + step;
|
||||
|
||||
var y0 = Terrain.HeightAt(x0);
|
||||
var y1 = Terrain.HeightAt(x1);
|
||||
var y = MathF.Min(y0, y1);
|
||||
|
||||
var depth = 200f;
|
||||
var p = ToScreen(new Vector2(x0, y));
|
||||
var w = ToScreen(step);
|
||||
var h = ToScreen(depth);
|
||||
|
||||
//_segments.Add(new Box
|
||||
//{
|
||||
// Anchor = Anchor.TopLeft,
|
||||
// Origin = Anchor.TopLeft,
|
||||
// Position = p,
|
||||
// Size = new Vector2(w + 1, h),
|
||||
// Colour = new Color4(80, 200, 120, 255)
|
||||
//});
|
||||
|
||||
_segments.Add(new Triangle
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Position = p,
|
||||
Size = new Vector2(w + 1, -h),
|
||||
Shear = new Vector2(x1 - x0, y1 - y0),
|
||||
Colour = new Color4(80, 200, 120, 255)
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i += 1)
|
||||
{
|
||||
var x0 = left + i * step;
|
||||
var x1 = x0 + step;
|
||||
|
||||
var y0 = Terrain.HeightAt(x0);
|
||||
var y1 = Terrain.HeightAt(x1);
|
||||
var y = (y0 + y1) / 2f;
|
||||
|
||||
var p = ToScreen(new Vector2(x0, y));
|
||||
_segments.Add(new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = p,
|
||||
Size = new Vector2(ToScreen(step) + 1, 3),
|
||||
Colour = new Color4(20, 120, 60, 255)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CarDrawable : WorldDrawable
|
||||
{
|
||||
private readonly Box _body;
|
||||
private readonly SimpleCar _car;
|
||||
private readonly Circle _wheelBack;
|
||||
private readonly Circle _wheelFront;
|
||||
|
||||
public CarDrawable(SimpleCar car)
|
||||
{
|
||||
_car = car;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
_wheelBack = new Circle { Colour = new Color4(25, 25, 25, 255) },
|
||||
_wheelFront = new Circle { Colour = new Color4(25, 25, 25, 255) },
|
||||
_body = new Box { Colour = new Color4(240, 70, 70, 255) }
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateFromWorld()
|
||||
{
|
||||
if (Camera is null) return;
|
||||
|
||||
var wheelR = SimpleCar.WheelRadius;
|
||||
var bodyW = 2.2f;
|
||||
var bodyH = 0.7f;
|
||||
|
||||
var wb = ToScreen(_car.WheelBackPos);
|
||||
var wf = ToScreen(_car.WheelFrontPos);
|
||||
|
||||
var rPx = ToScreen(wheelR);
|
||||
_wheelBack.Size = new Vector2(rPx * 2);
|
||||
_wheelBack.Position = wb - new Vector2(rPx);
|
||||
_wheelFront.Size = new Vector2(rPx * 2);
|
||||
_wheelFront.Position = wf - new Vector2(rPx);
|
||||
|
||||
var bp = ToScreen(_car.Position);
|
||||
_body.Size = new Vector2(ToScreen(bodyW), ToScreen(bodyH));
|
||||
_body.Origin = Anchor.Centre;
|
||||
_body.Anchor = Anchor.TopLeft;
|
||||
_body.Position = bp;
|
||||
_body.Rotation = -_car.RotationDegrees;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Terrain
|
||||
{
|
||||
public static float HeightAt(float x)
|
||||
{
|
||||
const float baseLine = 0f;
|
||||
|
||||
var h =
|
||||
0.9f * MathF.Sin(x * 0.55f) +
|
||||
0.6f * MathF.Sin(x * 0.18f + 1.3f) +
|
||||
0.25f * MathF.Sin(x * 1.15f);
|
||||
|
||||
var drift = 0.015f * x;
|
||||
|
||||
return baseLine + h + drift;
|
||||
}
|
||||
|
||||
public static float SlopeAt(float x)
|
||||
{
|
||||
const float eps = 0.02f;
|
||||
return (HeightAt(x + eps) - HeightAt(x - eps)) / (2 * eps);
|
||||
}
|
||||
|
||||
public static Vector2 NormalAt(float x)
|
||||
{
|
||||
var slope = SlopeAt(x);
|
||||
var n = new Vector2(-slope, 1f);
|
||||
return n.Normalized();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user