Initial CSharp Port

pull/3/head
Laura Sofia Heimann 2023-09-15 23:46:02 +07:00
parent 350761e8a2
commit 3b2a8c00a3
21 changed files with 385 additions and 287 deletions

5
.gitignore vendored

@ -12,4 +12,7 @@ export_presets.cfg
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
mono_crash.*.json
# IntelliJ IDE
.idea/

@ -0,0 +1,7 @@
<Project Sdk="Godot.NET.Sdk/4.1.1">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>StarterKit3DPlatformer</RootNamespace>
</PropertyGroup>
</Project>

@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Starter Kit 3D Platformer", "Starter Kit 3D Platformer.csproj", "{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{B6B2B9A6-A63D-4DA0-B770-B13C8179DFB0}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
EndGlobalSection
EndGlobal

@ -3,7 +3,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://0odt7cvj574e"
path.bptc="res://.godot/imported/colormap.png-c5d1d841e2d3dfd437d36ec333adedff.bptc.ctex"
path.bptc="res://.godot/imported/colormap.png-c1bc3c3aabeec406ff4b53328583776a.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
@ -11,8 +11,8 @@ metadata={
[deps]
source_file="res://models/textures/colormap.png"
dest_files=["res://.godot/imported/colormap.png-c5d1d841e2d3dfd437d36ec333adedff.bptc.ctex"]
source_file="res://models/Textures/colormap.png"
dest_files=["res://.godot/imported/colormap.png-c1bc3c3aabeec406ff4b53328583776a.bptc.ctex"]
[params]

@ -3,3 +3,9 @@
[ext_resource type="PackedScene" uid="uid://xy8rvnjp22n5" path="res://models/character.glb" id="1_vn7w5"]
[node name="character" instance=ExtResource("1_vn7w5")]
[node name="arm-left" parent="character2/root/torso" index="0"]
transform = Transform3D(0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 0, 0, 1, 0.3, 0.175, 0)
[node name="arm-right" parent="character2/root/torso" index="1"]
transform = Transform3D(0.707107, -0.707107, 0, 0.707107, 0.707107, 0, 0, 0, 1, -0.3, 0.1195, 0)

Binary file not shown.

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://dfpisimsgf5ce"]
[ext_resource type="Texture2D" uid="uid://0odt7cvj574e" path="res://models/textures/colormap.png" id="2_04qt4"]
[ext_resource type="Script" path="res://scripts/coin.gd" id="2_gaakh"]
[ext_resource type="Script" path="res://scripts/Coin.cs" id="1_717wj"]
[ext_resource type="Texture2D" uid="uid://0odt7cvj574e" path="res://models/Textures/colormap.png" id="2_04qt4"]
[ext_resource type="Texture2D" uid="uid://bs6puxrivhkk2" path="res://sprites/particle.png" id="3_xl1k0"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ct61v"]
@ -67,7 +67,7 @@ point_count = 3
colors = PackedColorArray(0.996094, 0.910156, 0.351563, 1, 1, 1, 1, 1)
[node name="coin" type="Area3D"]
script = ExtResource("2_gaakh")
script = ExtResource("1_717wj")
[node name="Mesh" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0)
@ -94,4 +94,4 @@ scale_amount_max = 0.15
scale_amount_curve = SubResource("Curve_6a7l3")
color_ramp = SubResource("Gradient_7tsk2")
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
[connection signal="body_entered" from="." to="." method="OnBodyEntered"]

@ -13,20 +13,24 @@ config_version=5
config/name="Starter Kit 3D Platformer"
config/tags=PackedStringArray("starterkit")
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.1", "Forward Plus")
config/features=PackedStringArray("4.1", "C#", "Forward Plus")
boot_splash/bg_color=Color(0.92549, 0.92549, 0.960784, 1)
boot_splash/image="res://splash-screen.png"
config/icon="res://icon.png"
[autoload]
Audio="*res://scripts/audio.gd"
Audio="*res://scripts/Audio.cs"
[display]
window/size/viewport_width=1280
window/size/viewport_height=720
[dotnet]
project/assembly_name="Starter Kit 3D Platformer"
[input]
move_right={

@ -1,18 +1,18 @@
[gd_scene load_steps=22 format=3 uid="uid://d0ymn6u246bv2"]
[ext_resource type="Script" path="res://scripts/player.gd" id="1_rvwhc"]
[ext_resource type="Script" path="res://scenes/Player.cs" id="1_somgi"]
[ext_resource type="PackedScene" uid="uid://c0e27836xgmhi" path="res://prefabs/character_prefab.tscn" id="2_g2iyx"]
[ext_resource type="Texture2D" uid="uid://8ggihh27mlrr" path="res://sprites/blob-shadow.png" id="3_gq1fq"]
[ext_resource type="ArrayMesh" uid="uid://deu06eho4c74" path="res://prefabs/cloud.res" id="4_dxhne"]
[ext_resource type="Script" path="res://scripts/view.gd" id="5_bg0fr"]
[ext_resource type="AudioStream" uid="uid://ddiy2lbypiwqv" path="res://audio/walking.ogg" id="5_wfvi2"]
[ext_resource type="Script" path="res://scripts/View.cs" id="6_aqfpb"]
[ext_resource type="PackedScene" uid="uid://cnymdajj1vsqm" path="res://prefabs/platform_prefab.tscn" id="6_sdmev"]
[ext_resource type="PackedScene" uid="uid://rjh4ifidqrfp" path="res://prefabs/platform_medium_prefab.tscn" id="7_ixpj3"]
[ext_resource type="PackedScene" uid="uid://uqr4hswv6d2g" path="res://prefabs/platform_grass_large_round_prefab.tscn" id="8_4oh1e"]
[ext_resource type="PackedScene" uid="uid://dtjvjdjl8cs6e" path="res://models/flag.glb" id="9_fw1f5"]
[ext_resource type="PackedScene" uid="uid://dfpisimsgf5ce" path="res://prefabs/coin_prefab.tscn" id="10_fwhys"]
[ext_resource type="Script" path="res://scripts/hud.gd" id="11_apvpm"]
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita-one-regular.ttf" id="11_dkqcs"]
[ext_resource type="Script" path="res://scripts/Hud.cs" id="12_47li6"]
[ext_resource type="Texture2D" uid="uid://cd7oyc56ehkx1" path="res://sprites/coin.png" id="13_jfda3"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_iakcd"]
@ -68,10 +68,10 @@ shadow_offset = Vector2(2, 2)
[node name="Environment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_kt478")
[node name="Player" type="CharacterBody3D" parent="." node_paths=PackedStringArray("view")]
[node name="Player" type="CharacterBody3D" parent="." node_paths=PackedStringArray("View")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
script = ExtResource("1_rvwhc")
view = NodePath("../View")
script = ExtResource("1_somgi")
View = NodePath("../View")
[node name="Collider" type="CollisionShape3D" parent="Player"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
@ -111,10 +111,10 @@ volume_db = -5.0
pitch_scale = 1.25
autoplay = true
[node name="View" type="Node3D" parent="." node_paths=PackedStringArray("target")]
[node name="View" type="Node3D" parent="." node_paths=PackedStringArray("Target")]
transform = Transform3D(0.707107, -0.298836, 0.640856, 0, 0.906308, 0.422618, -0.707107, -0.298836, 0.640856, 0, 0, 0)
script = ExtResource("5_bg0fr")
target = NodePath("../Player")
script = ExtResource("6_aqfpb")
Target = NodePath("../Player")
[node name="Camera" type="Camera3D" parent="View"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 10)
@ -164,7 +164,7 @@ shadow_enabled = true
shadow_opacity = 0.75
[node name="HUD" type="CanvasLayer" parent="."]
script = ExtResource("11_apvpm")
script = ExtResource("12_47li6")
[node name="Icon" type="TextureRect" parent="HUD"]
offset_left = 57.0
@ -190,6 +190,6 @@ offset_bottom = 123.0
text = "0"
label_settings = SubResource("LabelSettings_38ys3")
[connection signal="coin_collected" from="Player" to="HUD" method="_on_coin_collected"]
[connection signal="CoinCollected" from="Player" to="HUD" method="OnCoinCollected"]
[editable path="Player/Character"]

@ -0,0 +1,160 @@
using Godot;
using System;
namespace StarterKit3DPlatformer;
public partial class Player : CharacterBody3D, ICollector
{
[ExportSubgroup("Components")]
[Export] public Node3D View;
[ExportSubgroup("Properties")]
[Export] public float MovementSpeed = 200f;
[Export] public float JumpStrength = 6f;
private Vector3 m_movementVelocity;
private float m_rotationDirection;
private float m_gravity;
private bool m_previouslyFloored = false;
private bool m_jumpSingle = true;
private bool m_jumpDouble = true;
private int m_coins = 0;
private CpuParticles3D m_particlesTrail;
private AudioStreamPlayer m_soundFootsteps;
private Node3D m_model;
private AnimationPlayer m_animation;
[Signal]
public delegate void CoinCollectedEventHandler(int coins);
public override void _Ready()
{
m_particlesTrail = GetNode<CpuParticles3D>("ParticlesTrail");
m_soundFootsteps = GetNode<AudioStreamPlayer>("SoundFootsteps");
m_model = GetNode<Node3D>("Character");
m_animation = GetNode<AnimationPlayer>("Character/AnimationPlayer");
}
public override void _PhysicsProcess(double delta)
{
// Handle Functions
HandleControls(delta);
HandleGravity(delta);
HandleAnimations();
// Movement
var appliedVelocity = Velocity.Lerp(m_movementVelocity, (float)delta * 10f);
appliedVelocity.Y = -m_gravity;
Velocity = appliedVelocity;
MoveAndSlide();
// Rotation
if (Velocity.Length() > 0f)
{
m_rotationDirection = new Vector2(Velocity.Z, Velocity.X).Angle();
}
var newRotation = Rotation;
newRotation.Y = Mathf.LerpAngle(Rotation.Y, m_rotationDirection, (float)delta * 10f);
Rotation = newRotation;
// Scale Animation (Juice)
m_model.Scale = m_model.Scale.Lerp(new Vector3(1f, 1f, 1f), (float)delta * 10f);
// Animation when landing
if (IsOnFloor() && m_gravity > 2f && !m_previouslyFloored)
{
m_model.Scale = new Vector3(1.25f, 0.75f, 1.25f);
}
m_previouslyFloored = IsOnFloor();
}
private void HandleControls(double delta)
{
// Movement
var input = Vector3.Zero;
input.X = Input.GetAxis("move_left", "move_right");
input.Z = Input.GetAxis("move_forward", "move_back");
m_movementVelocity = View.Basis * input * MovementSpeed * (float)delta;
// Jumping
if (Input.IsActionJustPressed("jump"))
{
if (m_jumpSingle || m_jumpDouble)
{
Audio.Instance.Play("res://audio/jump.ogg");
}
if (m_jumpDouble)
{
m_gravity = -JumpStrength;
m_jumpDouble = false;
m_model.Scale = new Vector3(0.5f, 1.5f, 0.5f);
}
if (m_jumpSingle)
{
Jump();
}
}
}
private void HandleGravity(double delta)
{
m_gravity += 25f * (float)delta;
if (m_gravity > 0f && IsOnFloor())
{
m_jumpSingle = true;
m_gravity = 0f;
}
}
private void HandleAnimations()
{
m_particlesTrail.Emitting = false;
m_soundFootsteps.StreamPaused = true;
if (IsOnFloor())
{
if (Mathf.Abs(Velocity.X) > 1f || Mathf.Abs(Velocity.Z) > 1f)
{
m_animation.Play("walk", 0.5f);
m_particlesTrail.Emitting = true;
m_soundFootsteps.StreamPaused = false;
}
else
{
m_animation.Play("idle", 0.5f);
}
}
else
{
m_animation.Play("jump", 0.5f);
}
}
// Jumping
private void Jump()
{
m_gravity = -JumpStrength;
m_model.Scale = new Vector3(0.5f, 1.5f, 0.5f);
m_jumpSingle = false;
m_jumpDouble = true;
}
// Collecting Coins
public void CollectCoin()
{
m_coins += 1;
EmitSignal(SignalName.CoinCollected, m_coins);
}
}

@ -0,0 +1,54 @@
using Godot;
using System.Collections.Generic;
using System.Linq;
namespace StarterKit3DPlatformer;
public partial class Audio : Node
{
public static Audio Instance;
private int m_players = 12;
private string m_bus = "master";
private List<AudioStreamPlayer> m_available = new();
private List<string> m_queue = new();
public override void _EnterTree()
{
Instance = this;
}
public override void _Ready()
{
for (int i = 0; i < m_players; i++)
{
var audioStreamPlayer = new AudioStreamPlayer();
AddChild(audioStreamPlayer);
m_available.Add(audioStreamPlayer);
audioStreamPlayer.VolumeDb = -10;
audioStreamPlayer.Bus = m_bus;
}
}
public override void _Process(double delta)
{
if (m_queue.Count > 0 && m_available.Any(x => !x.Playing))
{
var queueItem = m_queue[0];
var audioStreamPlayer = m_available.First(x => !x.Playing);
audioStreamPlayer.Stream = GD.Load<AudioStream>(queueItem);
audioStreamPlayer.Play();
audioStreamPlayer.PitchScale = (float)GD.RandRange(0.9f, 1.1f);
m_queue.Remove(queueItem);
}
}
public void Play(string soundPath)
{
m_queue.Add(soundPath);
}
}

@ -0,0 +1,32 @@
using Godot;
namespace StarterKit3DPlatformer;
public partial class Coin : Area3D
{
private float m_time = 0f;
private bool m_grabbed = false;
public void OnBodyEntered(Node3D body)
{
if (!m_grabbed && body is ICollector collector)
{
collector.CollectCoin();
Audio.Instance.Play("res://audio/coin.ogg");
GetNode<Node>("Mesh").QueueFree();
GetNode<CpuParticles3D>("Particles").Emitting = false;
m_grabbed = true;
}
}
public override void _Process(double delta)
{
RotateY(2 * (float)delta);
Position += new Vector3(0f, (Mathf.Cos(m_time * 5f) * 1f) * (float)delta, 0f);
m_time += (float)delta;
}
}

@ -0,0 +1,18 @@
using Godot;
namespace StarterKit3DPlatformer;
public partial class Hud : Node
{
private Label m_coins;
public override void _Ready()
{
m_coins = GetNode<Label>("Coins");
}
public void OnCoinCollected(int coins)
{
m_coins.Text = coins.ToString();
}
}

@ -0,0 +1,6 @@
namespace StarterKit3DPlatformer;
public interface ICollector
{
void CollectCoin();
}

@ -0,0 +1,55 @@
using System;
using Godot;
namespace StarterKit3DPlatformer;
public partial class View : Node3D
{
[ExportGroup("Properties")]
[Export] public Node3D Target;
[ExportGroup("Zoom")]
[Export] public float ZoomMinimum = 16f;
[Export] public float ZoomMaximum = 4f;
[Export] public float ZoomSpeed = 10f;
[ExportGroup("Rotation")]
[Export] public float RotationSpeed = 120f;
private Vector3 m_cameraRotation;
private float m_zoom = 10f;
private Camera3D m_camera;
public override void _Ready()
{
base._Ready();
m_camera = GetNode<Camera3D>("Camera");
m_cameraRotation = RotationDegrees;
}
public override void _PhysicsProcess(double delta)
{
Position = Position.Lerp(Target.Position, (float)delta * 4f);
RotationDegrees = RotationDegrees.Lerp(m_cameraRotation, (float)delta * 6f);
m_camera.Position = m_camera.Position.Lerp(new Vector3(0f, 0f, m_zoom), 8f * (float)delta);
HandleInput(delta);
}
private void HandleInput(double delta)
{
// Rotation
var input = Vector3.Zero;
input.Y = Input.GetAxis("camera_left", "camera_right");
input.X = Input.GetAxis("camera_up", "camera_down");
m_cameraRotation += input * RotationSpeed * (float)delta;
m_cameraRotation.X = Mathf.Clamp(m_cameraRotation.X, -80, -10);
// Zooming
m_zoom += Input.GetAxis("zoom_in", "zoom_out") * ZoomSpeed * (float)delta;
m_zoom = Mathf.Clamp(m_zoom, ZoomMaximum, ZoomMinimum);
}
}

@ -1,36 +0,0 @@
extends Node
# Code adapted from KidsCanCode
var num_players = 12
var bus = "master"
var available = [] # The available players.
var queue = [] # The queue of sounds to play.
func _ready():
for i in num_players:
var p = AudioStreamPlayer.new()
add_child(p)
available.append(p)
p.volume_db = -10
p.connect("finished", Callable(self, "_on_stream_finished").bind(p))
p.bus = bus
func _on_stream_finished(stream): available.append(stream)
func play(sound_path): queue.append(sound_path)
func _process(_delta):
if not queue.is_empty() and not available.is_empty():
available[0].stream = load(queue.pop_front())
available[0].play()
available[0].pitch_scale = randf_range(0.9, 1.1)
available.pop_front()

@ -1,27 +0,0 @@
extends Area3D
var time = 0
var grabbed = false
# Collecting coins
func _on_body_entered(body):
if body.has_method("collect_coin") and !grabbed:
body.collect_coin()
Audio.play("res://audio/coin.ogg") # Play sound
$Mesh.queue_free()
$Particles.emitting = false
grabbed = true
# Rotating, animating up and down
func _process(delta):
rotate_y(2 * delta) # Rotation
position.y += (cos(time * 5) * 1) * delta # Sine movement
time += delta

@ -1,5 +0,0 @@
extends Node
func _on_coin_collected(coins):
$Coins.text = str(coins)

@ -1,145 +0,0 @@
extends CharacterBody3D
signal coin_collected
@export_subgroup("Components")
@export var view: Node3D
@export_subgroup("Properties")
@export var movement_speed = 200
@export var jump_strength = 6
var movement_velocity: Vector3
var rotation_direction: float
var gravity = 0
var previously_floored = false
var jump_single = true
var jump_double = true
var coins = 0
@onready var particles_trail = $ParticlesTrail
@onready var sound_footsteps = $SoundFootsteps
@onready var model = $Character
@onready var animation = $Character/AnimationPlayer
# Functions
func _ready():
pass
func _physics_process(delta):
# Handle functions
handle_controls(delta)
handle_gravity(delta)
handle_animations()
# Movement
var applied_velocity: Vector3
applied_velocity = velocity.lerp(movement_velocity, delta * 10)
applied_velocity.y = -gravity
velocity = applied_velocity
move_and_slide()
# Rotation
if velocity.length() > 0:
rotation_direction = Vector2(velocity.z, velocity.x).angle()
rotation.y = lerp_angle(rotation.y, rotation_direction, delta * 10)
# Scale animation (juice)
model.scale = model.scale.lerp(Vector3(1, 1, 1), delta * 10)
# Animation when landing
if is_on_floor() and gravity > 2 and !previously_floored:
model.scale = Vector3(1.25, 0.75, 1.25)
previously_floored = is_on_floor()
# Handle animation(s)
func handle_animations():
particles_trail.emitting = false
sound_footsteps.stream_paused = true
if is_on_floor():
if abs(velocity.x) > 1 or abs(velocity.z) > 1:
animation.play("walk", 0.5)
particles_trail.emitting = true
sound_footsteps.stream_paused = false
else:
animation.play("idle", 0.5)
else:
animation.play("jump", 0.5)
# Handle movement input
func handle_controls(delta):
# Movement
var input := Vector3.ZERO
input.x = Input.get_axis("move_left", "move_right")
input.z = Input.get_axis("move_forward", "move_back")
movement_velocity = view.basis * input * movement_speed * delta
# Jumping
if Input.is_action_just_pressed("jump"):
if jump_single or jump_double:
Audio.play("res://audio/jump.ogg")
if jump_double:
gravity = -jump_strength
jump_double = false
model.scale = Vector3(0.5, 1.5, 0.5)
if(jump_single): jump()
# Handle gravity
func handle_gravity(delta):
gravity += 25 * delta
if gravity > 0 and is_on_floor():
jump_single = true
gravity = 0
# Jumping
func jump():
gravity = -jump_strength
model.scale = Vector3(0.5, 1.5, 0.5)
jump_single = false;
jump_double = true;
# Collecting coins
func collect_coin():
coins += 1
emit_signal("coin_collected", coins)

@ -1,53 +0,0 @@
extends Node3D
@export_group("Properties")
@export var target: Node
@export_group("Zoom")
@export var zoom_minimum = 16
@export var zoom_maximum = 4
@export var zoom_speed = 10
@export_group("Rotation")
@export var rotation_speed = 120
var camera_rotation:Vector3
var zoom = 10
@onready var camera = $Camera
func _ready():
camera_rotation = rotation_degrees # Initial rotation
pass
func _physics_process(delta):
# Set position and rotation to targets
self.position = self.position.lerp(target.position, delta * 4)
rotation_degrees = rotation_degrees.lerp(camera_rotation, delta * 6)
camera.position = camera.position.lerp(Vector3(0, 0, zoom), 8 * delta)
handle_input(delta)
# Handle input
func handle_input(delta):
# Rotation
var input := Vector3.ZERO
input.y = Input.get_axis("camera_left", "camera_right")
input.x = Input.get_axis("camera_up", "camera_down")
camera_rotation += input * rotation_speed * delta
camera_rotation.x = clamp(camera_rotation.x, -80, -10)
# Zooming
zoom += Input.get_axis("zoom_in", "zoom_out") * zoom_speed * delta
zoom = clamp(zoom, zoom_maximum, zoom_minimum)