Initial commit
This commit is contained in:
21
source/ComponentUtils.cs
Normal file
21
source/ComponentUtils.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FactoryGame;
|
||||
|
||||
public class ComponentUtils {
|
||||
public static Array<T> GetComponents<[MustBeVariant] T>(Node node, int iterations = 1) where T : IComponent {
|
||||
var result = new Array<T>();
|
||||
|
||||
var children = node.GetChildren();
|
||||
foreach (var item in children) {
|
||||
if (item is T component)
|
||||
result.Add(component);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public static T? GetComponent<[MustBeVariant] T>(Node node, int index = 0) where T : IComponent =>
|
||||
GetComponents<T>(node)[index];
|
||||
}
|
||||
1
source/ComponentUtils.cs.uid
Normal file
1
source/ComponentUtils.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ruibfbsgsvnv
|
||||
3
source/Entities/IComponent.cs
Normal file
3
source/Entities/IComponent.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace FactoryGame.Entities;
|
||||
|
||||
public interface IComponent { }
|
||||
1
source/Entities/IComponent.cs.uid
Normal file
1
source/Entities/IComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://01l4dxjmuufm
|
||||
7
source/Entities/PlayerMovingState.cs
Normal file
7
source/Entities/PlayerMovingState.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace FactoryGame.Entities;
|
||||
|
||||
public enum PlayerMovingState {
|
||||
Walking,
|
||||
Crouching,
|
||||
Sprinting
|
||||
}
|
||||
1
source/Entities/PlayerMovingState.cs.uid
Normal file
1
source/Entities/PlayerMovingState.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bw5i8tvduw6xb
|
||||
47
source/Scripts/CameraController.cs
Normal file
47
source/Scripts/CameraController.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Godot;
|
||||
|
||||
using static FactoryGame.Utils.Constants.PropertyHints;
|
||||
|
||||
public partial class CameraController : Camera3D {
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float MouseSensitivity { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float JoystickSensitivity { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float DeadZone { get; set; } = 0.1f;
|
||||
|
||||
private float _lastX = 0f;
|
||||
private float _lastY = 0f;
|
||||
|
||||
private Node3D? _parent;
|
||||
|
||||
public override void _Ready() {
|
||||
_parent = GetParent<Node3D>();
|
||||
Input.MouseMode = Input.MouseModeEnum.Captured;
|
||||
}
|
||||
|
||||
public override void _Process(double delta) {
|
||||
var input = Input.GetVector("camera_left", "camera_right", "camera_up", "camera_down", DeadZone);
|
||||
RotateCamera(input * -(JoystickSensitivity / 10f));
|
||||
|
||||
base._Process(delta);
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event) {
|
||||
if (@event is InputEventMouseMotion mouseMotion)
|
||||
RotateCamera(new Vector2(mouseMotion.Relative.X, mouseMotion.Relative.Y) * -(MouseSensitivity / 100f));
|
||||
|
||||
base._Input(@event);
|
||||
}
|
||||
|
||||
private void RotateCamera(Vector2 direction) {
|
||||
if (direction == Vector2.Zero) return;
|
||||
|
||||
_parent?.RotateObjectLocal(Vector3.Up, direction.X);
|
||||
//_parent!.RotateY(direction.X);
|
||||
|
||||
var rotationX = Rotation.X + direction.Y;
|
||||
rotationX = Mathf.Clamp(rotationX, Mathf.DegToRad(-90), Mathf.DegToRad(90));
|
||||
Rotation = new Vector3(rotationX, Rotation.Y, Rotation.Z);
|
||||
}
|
||||
}
|
||||
1
source/Scripts/CameraController.cs.uid
Normal file
1
source/Scripts/CameraController.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cyl680l0b3hej
|
||||
75
source/Scripts/Components/CollisionComponent.cs
Normal file
75
source/Scripts/Components/CollisionComponent.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using FactoryGame.Entities;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public partial class CollisionComponent : Area3D, IComponent {
|
||||
public enum CollisionComponentMode {
|
||||
Body,
|
||||
BodyShape,
|
||||
Area,
|
||||
AreaShape
|
||||
}
|
||||
[Export]
|
||||
public CollisionComponentMode Mode { get; set; } = CollisionComponentMode.Body;
|
||||
[ExportGroup("White List")]
|
||||
[Export]
|
||||
public bool UseWhiteList { get; set; }
|
||||
[Export]
|
||||
public Node3D[]? WhiteList { get; set; }
|
||||
[ExportGroup("Black List")]
|
||||
[Export]
|
||||
public bool UseBlackList { get; set; }
|
||||
[Export]
|
||||
public Node3D[]? BlackList { get; set; }
|
||||
|
||||
public IReadOnlyList<Node3D> Collisions => _collisions;
|
||||
private List<Node3D> _collisions = new();
|
||||
public override void _Ready() {
|
||||
switch (Mode) {
|
||||
case CollisionComponentMode.Body: {
|
||||
BodyEntered += (body) => Enter(body);
|
||||
BodyExited += (body) => Exit(body);
|
||||
}
|
||||
; break;
|
||||
case CollisionComponentMode.BodyShape: {
|
||||
BodyShapeEntered += (_, body, _, _) => Enter(body);
|
||||
BodyShapeExited += (_, body, _, _) => Exit(body);
|
||||
}
|
||||
; break;
|
||||
case CollisionComponentMode.Area: {
|
||||
AreaEntered += Enter;
|
||||
AreaEntered += Exit;
|
||||
}
|
||||
; break;
|
||||
case CollisionComponentMode.AreaShape: {
|
||||
AreaShapeEntered += (_, body, _, _) => Enter(body);
|
||||
AreaShapeEntered += (_, body, _, _) => Exit(body);
|
||||
}
|
||||
; break;
|
||||
}
|
||||
|
||||
base._Ready();
|
||||
}
|
||||
|
||||
private void Enter(Node3D body) {
|
||||
if (CheckBody(body))
|
||||
_collisions.Add(body);
|
||||
}
|
||||
private void Exit(Node3D body) {
|
||||
if (_collisions.Contains(body))
|
||||
_collisions.Remove(body);
|
||||
}
|
||||
|
||||
private bool CheckBody(Node3D body) {
|
||||
if (UseBlackList && BlackList != null && BlackList.Contains(body))
|
||||
return false;
|
||||
if (UseWhiteList && WhiteList != null && !WhiteList.Contains(body))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsCollide() => _collisions.Count > 0;
|
||||
public bool IsCollide(Node3D body) => _collisions.Contains(body);
|
||||
}
|
||||
1
source/Scripts/Components/CollisionComponent.cs.uid
Normal file
1
source/Scripts/Components/CollisionComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b1683v4e4gh38
|
||||
20
source/Scripts/DebugScreen.cs
Normal file
20
source/Scripts/DebugScreen.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Godot;
|
||||
|
||||
public partial class DebugScreen : Control {
|
||||
public static DebugScreen? Instance { get; private set; }
|
||||
|
||||
[Export]
|
||||
public RichTextLabel? Text { get; set; }
|
||||
|
||||
private string layout = string.Empty;
|
||||
public override void _Ready() {
|
||||
layout = Text!.Text;
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void _Process(double delta) {
|
||||
var text = layout;
|
||||
Text!.Text = layout.Replace("{FPS}", Engine.GetFramesPerSecond().ToString());
|
||||
base._Process(delta);
|
||||
}
|
||||
}
|
||||
1
source/Scripts/DebugScreen.cs.uid
Normal file
1
source/Scripts/DebugScreen.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bxfhkc44lo88c
|
||||
193
source/Scripts/PlayerController.cs
Normal file
193
source/Scripts/PlayerController.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using FactoryGame.Entities;
|
||||
using Godot;
|
||||
|
||||
using static FactoryGame.Utils.Constants.PropertyHints;
|
||||
|
||||
public partial class PlayerController : CharacterBody3D {
|
||||
[ExportGroup("Nodes")]
|
||||
[Export]
|
||||
public CollisionShape3D? StandCollision { get; set; }
|
||||
[Export]
|
||||
public CollisionShape3D? CrouchCollision { get; set; }
|
||||
[Export]
|
||||
public CollisionComponent? HeadCollisionComponent { get; set; }
|
||||
[Export]
|
||||
public CollisionComponent? BottomCollisionComponent { get; set; }
|
||||
[Export]
|
||||
public Camera3D? Camera { get; set; }
|
||||
[Export]
|
||||
public Marker3D? StandCameraPosition { get; set; }
|
||||
[Export]
|
||||
public Marker3D? CrouchCameraPosition { get; set; }
|
||||
|
||||
[ExportGroup("Behavior")]
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CameraSpeed { get; set; } = 0.25f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float PushImpulse { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CeilingBounce { get; set; } = 0.3f;
|
||||
[Export]
|
||||
public PlayerMovingState MovingState {
|
||||
get => _movingState; set {
|
||||
if (_movingState == value) return;
|
||||
if (crouchLock) { _movingState = PlayerMovingState.Crouching; return; }
|
||||
|
||||
_movingState = value;
|
||||
StandCollision!.Disabled = !(_movingState == PlayerMovingState.Walking || _movingState == PlayerMovingState.Sprinting);
|
||||
CrouchCollision!.Disabled = _movingState != PlayerMovingState.Crouching;
|
||||
if (_movingState == PlayerMovingState.Walking || _movingState == PlayerMovingState.Sprinting)
|
||||
_cameraTargetPosition = StandCameraPosition!.Position;
|
||||
if (_movingState == PlayerMovingState.Crouching)
|
||||
_cameraTargetPosition = CrouchCameraPosition!.Position;
|
||||
}
|
||||
}
|
||||
private PlayerMovingState _movingState = PlayerMovingState.Walking;
|
||||
private bool crouchLock = false;
|
||||
|
||||
[ExportSubgroup("Gravity Stabilization")]
|
||||
[Export(PropertyHint.Range, TwoOctaveRange)]
|
||||
public float FlyGravityStabilizationSpeed { get; set; } = 2f;
|
||||
[Export(PropertyHint.Range, TwoOctaveRange)]
|
||||
public float FloorGravityStabilizationSpeed { get; set; } = 15f;
|
||||
public float GetCurrentGravityStabilizationSpeed() => BottomCollisionComponent!.IsCollide() ? FloorGravityStabilizationSpeed : FlyGravityStabilizationSpeed;
|
||||
|
||||
[ExportSubgroup("Movement")]
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float MoveVelocity { get; set; } = 4f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float CrouchVelocity { get; set; } = 2f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float SprintVelocity { get; set; } = 8f;
|
||||
[Export(PropertyHint.Range, QuartRange)]
|
||||
public float FlyVelocityMultiplier { get; set; } = 1.05f;
|
||||
private float GetCurrentSpeed() {
|
||||
var speed = MovingState switch {
|
||||
PlayerMovingState.Walking => MoveVelocity,
|
||||
PlayerMovingState.Crouching => CrouchVelocity,
|
||||
PlayerMovingState.Sprinting => SprintVelocity,
|
||||
_ => MoveVelocity
|
||||
};
|
||||
|
||||
if (!IsOnFloor()) return FlyVelocityMultiplier * speed;
|
||||
return speed;
|
||||
}
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float JumpVelocity { get; set; } = 4f;
|
||||
|
||||
[ExportSubgroup("Friction")]
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float MoveFriction { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CrouchFriction { get; set; } = 0.2f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float SprintFriction { get; set; } = 0.6f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float FlyFriction { get; set; } = 0.1f;
|
||||
private float GetCurrentFriction() => !IsOnFloor() ? FlyFriction : MovingState switch {
|
||||
PlayerMovingState.Walking => MoveFriction,
|
||||
PlayerMovingState.Crouching => CrouchFriction,
|
||||
PlayerMovingState.Sprinting => SprintFriction,
|
||||
_ => MoveFriction
|
||||
};
|
||||
|
||||
public override void _Ready() {
|
||||
_cameraTargetPosition = Camera!.Position;
|
||||
HeadCollisionComponent!.BodyEntered += (_) => ProceedCrouchingCheck(HeadCollisionComponent!);
|
||||
HeadCollisionComponent!.BodyExited += (_) => ProceedCrouchingCheck(HeadCollisionComponent!);
|
||||
|
||||
base._Ready();
|
||||
}
|
||||
public override void _PhysicsProcess(double delta) {
|
||||
var fDelta = (float)delta;
|
||||
UpdatePlayerOrientation(fDelta);
|
||||
|
||||
var gravity = ProceedGravityVelocity(fDelta);
|
||||
var move = ProceedMoveVelocity(fDelta);
|
||||
|
||||
Velocity = gravity + move;
|
||||
MoveAndSlide();
|
||||
|
||||
Push();
|
||||
|
||||
base._PhysicsProcess(delta);
|
||||
}
|
||||
|
||||
private void ProceedCrouchingCheck(CollisionComponent collisionComponent) {
|
||||
if (collisionComponent.IsCollide()) {
|
||||
crouchLock = true;
|
||||
MovingState = PlayerMovingState.Crouching;
|
||||
} else crouchLock = false;
|
||||
}
|
||||
|
||||
private void UpdatePlayerOrientation(float delta) {
|
||||
var gravity = GetGravity();
|
||||
if (gravity == Vector3.Zero) return;
|
||||
|
||||
UpDirection = -gravity;
|
||||
var objectBottomDirection = -Transform.Basis.Y;
|
||||
var angleToTarget = objectBottomDirection.AngleTo(gravity);
|
||||
|
||||
var rotationAxis = objectBottomDirection.Cross(gravity).Normalized();
|
||||
|
||||
var interpolatedAngle = Mathf.Lerp(0, angleToTarget, GetCurrentGravityStabilizationSpeed() * delta);
|
||||
if (rotationAxis.IsNormalized())
|
||||
Rotate(rotationAxis, interpolatedAngle);
|
||||
}
|
||||
|
||||
private Vector3 _gravityVelocity = Vector3.Zero;
|
||||
private Vector3 ProceedGravityVelocity(float delta) {
|
||||
var localUp = GlobalTransform.Basis.Y;
|
||||
var gravityForce = GetGravity();
|
||||
|
||||
if (IsOnFloor()) {
|
||||
_gravityVelocity = gravityForce.Normalized() * 0.1f;
|
||||
|
||||
if (Input.IsActionPressed("move_jump"))
|
||||
_gravityVelocity = localUp * JumpVelocity;
|
||||
|
||||
return _gravityVelocity;
|
||||
}
|
||||
|
||||
if (IsOnCeiling() && GetSlideCollisionCount() > 0) {
|
||||
var normal = GetLastSlideCollision().GetNormal();
|
||||
if (_gravityVelocity.Dot(localUp) > 0 && normal.Dot(localUp) < -0.5f)
|
||||
_gravityVelocity = _gravityVelocity.Bounce(normal) * CeilingBounce;
|
||||
}
|
||||
|
||||
_gravityVelocity += gravityForce * delta;
|
||||
return _gravityVelocity;
|
||||
}
|
||||
|
||||
private Vector3 _cameraTargetPosition;
|
||||
private Vector3 _moveVelocity = Vector3.Zero;
|
||||
private Vector3 ProceedMoveVelocity(float delta) {
|
||||
var movingState = Input.IsActionPressed("action_sprint") ? PlayerMovingState.Sprinting : PlayerMovingState.Walking;
|
||||
movingState = Input.IsActionPressed("action_crouch") ? PlayerMovingState.Crouching : movingState;
|
||||
MovingState = movingState;
|
||||
|
||||
Camera!.Position = Camera!.Position.Lerp(_cameraTargetPosition, CameraSpeed);
|
||||
|
||||
var inputDir = Input.GetVector("move_left", "move_right", "move_forward", "move_backward");
|
||||
|
||||
var dir = (GlobalTransform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
|
||||
|
||||
if (inputDir == Vector2.Zero)
|
||||
_moveVelocity = _moveVelocity.Lerp(Vector3.Zero, GetCurrentFriction());
|
||||
else
|
||||
_moveVelocity = dir * GetCurrentSpeed();
|
||||
|
||||
return _moveVelocity;
|
||||
}
|
||||
|
||||
private void Push() {
|
||||
for (int i = 0; i < GetSlideCollisionCount(); i++) {
|
||||
var collision = GetSlideCollision(i);
|
||||
if (collision.GetCollider() is RigidBody3D body) {
|
||||
var pushDir = -collision.GetNormal();
|
||||
|
||||
body.ApplyCentralImpulse(pushDir * _moveVelocity.Length() * PushImpulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
source/Scripts/PlayerController.cs.uid
Normal file
1
source/Scripts/PlayerController.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chuyrnjbwvvf5
|
||||
11
source/Utils/Constants.cs
Normal file
11
source/Utils/Constants.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace FactoryGame.Utils;
|
||||
|
||||
public static class Constants {
|
||||
public static class PropertyHints {
|
||||
public const string PrimaRange = "0, 1, 0.05";
|
||||
public const string SecondRange = "0, 2, 0.1";
|
||||
public const string QuartRange = "0, 4, 0.2";
|
||||
public const string OctaveRange = "0, 8, 0.4";
|
||||
public const string TwoOctaveRange = "0, 16, 0.8";
|
||||
}
|
||||
}
|
||||
1
source/Utils/Constants.cs.uid
Normal file
1
source/Utils/Constants.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cdy0yx7vgsl64
|
||||
Reference in New Issue
Block a user