Initial commit

This commit is contained in:
AXOLOTsh
2026-03-01 08:32:24 +03:00
commit 80e7624617
44 changed files with 2351 additions and 0 deletions

21
source/ComponentUtils.cs Normal file
View 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];
}

View File

@@ -0,0 +1 @@
uid://ruibfbsgsvnv

View File

@@ -0,0 +1,3 @@
namespace FactoryGame.Entities;
public interface IComponent { }

View File

@@ -0,0 +1 @@
uid://01l4dxjmuufm

View File

@@ -0,0 +1,7 @@
namespace FactoryGame.Entities;
public enum PlayerMovingState {
Walking,
Crouching,
Sprinting
}

View File

@@ -0,0 +1 @@
uid://bw5i8tvduw6xb

View 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);
}
}

View File

@@ -0,0 +1 @@
uid://cyl680l0b3hej

View 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);
}

View File

@@ -0,0 +1 @@
uid://b1683v4e4gh38

View 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);
}
}

View File

@@ -0,0 +1 @@
uid://bxfhkc44lo88c

View 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);
}
}
}
}

View File

@@ -0,0 +1 @@
uid://chuyrnjbwvvf5

11
source/Utils/Constants.cs Normal file
View 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";
}
}

View File

@@ -0,0 +1 @@
uid://cdy0yx7vgsl64