1
0
forked from joey/godottest

finish example game (minus ui lol)

This commit is contained in:
Joey Eamigh 2025-10-10 18:48:42 -04:00
parent e37896ea8e
commit 4c39bd120b
No known key found for this signature in database
GPG Key ID: CE8C05DFFC53C9CB
9 changed files with 296 additions and 28 deletions

View File

@ -66,5 +66,8 @@ reloadable = true
"windows.editor.x86_64" = "res://../rust/target/x86_64-pc-windows-msvc/debug/godottest_rs.dll"
[icons]
MainScene = "res://addons/rust/NodeRustFerris.svg"
CrashFucker = "res://addons/rust/NodeRustFerris.svg"
Mob = "res://addons/rust/NodeRustFerris.svg"
Player = "res://addons/rust/NodeRustFerris.svg"
AsyncRuntime = "res://addons/rust/NodeRustFerris.svg"

64
godot/mob.tscn Normal file
View File

@ -0,0 +1,64 @@
[gd_scene load_steps=9 format=3 uid="uid://dnn7s3hr6nkxo"]
[ext_resource type="Texture2D" uid="uid://4s7lh46ylxx8" path="res://art/enemyFlyingAlt_1.png" id="1_b3mxk"]
[ext_resource type="Texture2D" uid="uid://cco4veogb0frk" path="res://art/enemyFlyingAlt_2.png" id="2_1qmh0"]
[ext_resource type="Texture2D" uid="uid://cjcxwln2jke6f" path="res://art/enemySwimming_1.png" id="3_gfurk"]
[ext_resource type="Texture2D" uid="uid://cgtr6ouepyo2u" path="res://art/enemySwimming_2.png" id="4_ieysi"]
[ext_resource type="Texture2D" uid="uid://bs171ydu8mvir" path="res://art/enemyWalking_1.png" id="5_cixyi"]
[ext_resource type="Texture2D" uid="uid://ogi16vdypeh3" path="res://art/enemyWalking_2.png" id="6_7ulmv"]
[sub_resource type="SpriteFrames" id="SpriteFrames_pcqmr"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("1_b3mxk")
}, {
"duration": 1.0,
"texture": ExtResource("2_1qmh0")
}],
"loop": true,
"name": &"fly",
"speed": 3.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("3_gfurk")
}, {
"duration": 1.0,
"texture": ExtResource("4_ieysi")
}],
"loop": true,
"name": &"swim",
"speed": 3.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("5_cixyi")
}, {
"duration": 1.0,
"texture": ExtResource("6_7ulmv")
}],
"loop": true,
"name": &"walk",
"speed": 3.0
}]
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_jbnni"]
radius = 34.0
height = 82.0
[node name="Mob" type="Mob"]
collision_mask = 0
gravity_scale = 0.0
metadata/_edit_group_ = true
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
scale = Vector2(0.75, 0.75)
sprite_frames = SubResource("SpriteFrames_pcqmr")
animation = &"fly"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
rotation = 1.5707964
shape = SubResource("CapsuleShape2D_jbnni")
[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="."]

33
godot/scene.tscn Normal file
View File

@ -0,0 +1,33 @@
[gd_scene load_steps=4 format=3 uid="uid://nn237gseigsq"]
[ext_resource type="PackedScene" uid="uid://dnn7s3hr6nkxo" path="res://mob.tscn" id="1_ulcgi"]
[ext_resource type="PackedScene" uid="uid://bx7xynpbusro6" path="res://player.tscn" id="2_nxogm"]
[sub_resource type="Curve2D" id="Curve2D_drvgu"]
_data = {
"points": PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480, 0, 0, 0, 0, 0, 480, 720, 0, 0, 0, 0, 0, 720, 0, 0, 0, 0, 0, 0)
}
point_count = 5
[node name="MainScene" type="MainScene"]
mob_scene = ExtResource("1_ulcgi")
[node name="Player" parent="." instance=ExtResource("2_nxogm")]
speed = 100.0
[node name="MobTimer" type="Timer" parent="."]
wait_time = 0.5
[node name="ScoreTimer" type="Timer" parent="."]
[node name="StartTimer" type="Timer" parent="."]
wait_time = 2.0
one_shot = true
[node name="StartPosition" type="Marker2D" parent="."]
position = Vector2(240, 450)
[node name="MobPath" type="Path2D" parent="."]
curve = SubResource("Curve2D_drvgu")
[node name="MobSpawnLocation" type="PathFollow2D" parent="MobPath"]

24
rust/Cargo.lock generated
View File

@ -87,8 +87,7 @@ dependencies = [
[[package]]
name = "gdextension-api"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25d88dabe9fdb2e064cb545312178ec4025eefb62d9a3bbce1769088e2c381d"
source = "git+https://github.com/godot-rust/godot4-prebuilt?branch=release-v0.3#f70baca72747869aaac7781a0cf139f53a2aac19"
[[package]]
name = "gimli"
@ -111,8 +110,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "godot"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90b20e19a25e45460c4fd2b11b519beea3e9bcda929c1566f8d17a369b41dd82"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"godot-core",
"godot-macros",
@ -121,8 +119,7 @@ dependencies = [
[[package]]
name = "godot-bindings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "508d56d01018c16b67a906bda8bcd9ade7f265990e4be7c2dffecbfdebcc3992"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"gdextension-api",
]
@ -130,14 +127,12 @@ dependencies = [
[[package]]
name = "godot-cell"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1794fbec9934ef375d717d02ec142d9e0c7d7482b24d7da463264b92bc14eaf"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
[[package]]
name = "godot-codegen"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791e05ae1859028c3a910aaed83e4910ab7701ce32fa663d2dc7cd957287cdf5"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"godot-bindings",
"heck",
@ -150,8 +145,7 @@ dependencies = [
[[package]]
name = "godot-core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b1717ffba78bd2655c9ee1073752ac90d969b8a614800c469f00fc3ddc02439"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"glam",
"godot-bindings",
@ -163,8 +157,7 @@ dependencies = [
[[package]]
name = "godot-ffi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419e46ba92fba076da67f35ad96faaa830c1de4e8175db2bb4251aeb58b14b08"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"godot-bindings",
"godot-codegen",
@ -175,8 +168,7 @@ dependencies = [
[[package]]
name = "godot-macros"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a464e26854e63825d36655759c24556c970a8777535466ebf4ecc7f8e87a4638"
source = "git+https://github.com/godot-rust/gdext?branch=master#4a582106a2162ebb1fd0258c84c017710a130407"
dependencies = [
"godot-bindings",
"proc-macro2",

View File

@ -7,7 +7,9 @@ edition = "2024"
crate-type = ["cdylib"]
[dependencies]
godot = { version = "0.4.0", features = ["api-4-5"] }
godot = { git = "https://github.com/godot-rust/gdext", branch = "master", features = [
"api-4-5",
] }
tokio = { version = "1.47.1", features = ["full"] }
[build-dependencies]

View File

@ -2,7 +2,9 @@ use godot::{classes::Engine, prelude::*};
mod runtime;
mod mob;
mod player;
mod scene;
struct MyExtension;

41
rust/src/mob.rs Normal file
View File

@ -0,0 +1,41 @@
use godot::classes::{AnimatedSprite2D, IRigidBody2D, RigidBody2D};
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base=RigidBody2D)]
pub struct Mob {
// required
base: Base<RigidBody2D>,
}
#[godot_api]
impl IRigidBody2D for Mob {
fn init(base: Base<RigidBody2D>) -> Self {
Self { base }
}
fn ready(&mut self) {
self
.signals()
.body_exited()
.connect_self(Self::on_visible_on_screen_notifier_2d_screen_exited);
let mut animated_sprite_2d = self.base().get_node_as::<AnimatedSprite2D>("AnimatedSprite2D");
let types = animated_sprite_2d
.get_sprite_frames()
.expect("failed to get sprite frames??")
.get_animation_names();
let animation = Array::from(&types).pick_random().expect("no animations found");
animated_sprite_2d.set_animation(animation.stringify().arg());
animated_sprite_2d.play();
}
}
#[godot_api]
impl Mob {
fn on_visible_on_screen_notifier_2d_screen_exited(&mut self, _body: Gd<Node>) {
self.base_mut().queue_free();
}
}

View File

@ -1,11 +1,16 @@
use godot::classes::{AnimatedSprite2D, Area2D, IArea2D, Input};
use std::f32::consts::PI;
use godot::classes::{AnimatedSprite2D, Area2D, CollisionShape2D, IArea2D, Input};
use godot::global::atan2;
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base=Area2D)]
struct Player {
pub struct Player {
#[export]
speed: f32,
#[export]
velocity: Vector2,
screen_size: Rect2,
// required
@ -17,6 +22,7 @@ impl IArea2D for Player {
fn init(base: Base<Area2D>) -> Self {
Self {
speed: 400.0,
velocity: Vector2::ZERO,
screen_size: Rect2::default(),
base,
@ -25,37 +31,64 @@ impl IArea2D for Player {
fn ready(&mut self) {
self.screen_size = self.base().get_viewport_rect();
self.base_mut().hide();
self.signals().body_entered().connect_self(Self::body_entered);
}
fn process(&mut self, delta: f32) {
let input = Input::singleton();
let mut animated_sprite_2d = self.base().get_node_as::<AnimatedSprite2D>("AnimatedSprite2D");
let mut velocity = Vector2::default();
if input.is_action_pressed("move_right") {
velocity.x += 1.0;
self.velocity.x += 1.0 * self.speed;
}
if input.is_action_pressed("move_left") {
velocity.x -= 1.0;
self.velocity.x -= 1.0 * self.speed;
}
if input.is_action_pressed("move_down") {
velocity.y += 1.0;
self.velocity.y += 1.0 * self.speed;
}
if input.is_action_pressed("move_up") {
velocity.y -= 1.0;
self.velocity.y -= 1.0 * self.speed;
}
if velocity.length() > 0.0 {
velocity = velocity.normalized() * self.speed;
if self.velocity.length() > 0.0 {
animated_sprite_2d.set_animation("up");
animated_sprite_2d.set_rotation(atan2(self.velocity.y as f64, self.velocity.x as f64) as f32 + (PI / 2.0));
animated_sprite_2d.play();
} else {
animated_sprite_2d.stop();
animated_sprite_2d.set_frame(0);
}
let mut position = self.base().get_position();
position = (position + (velocity * delta)).clamp(Vector2::ZERO, self.screen_size.size);
position = (position + (self.velocity * delta)).clamp(Vector2::ZERO, self.screen_size.size);
self.velocity *= 0.9;
self.base_mut().set_position(position);
}
}
#[godot_api]
impl Player {
#[signal]
pub fn hit();
fn body_entered(&mut self, _body: Gd<Node2D>) {
let mut collision_shape_2d = self.base().get_node_as::<CollisionShape2D>("CollisionShape2D");
self.base_mut().hide();
self.signals().hit().emit();
collision_shape_2d.set_deferred("disabled", &Variant::from(true));
}
#[func]
pub fn start(&mut self, position: Vector2) {
let mut collision_shape_2d = self.base().get_node_as::<CollisionShape2D>("CollisionShape2D");
self.base_mut().set_position(position);
self.base_mut().show();
collision_shape_2d.set_disabled(false);
}
}

98
rust/src/scene.rs Normal file
View File

@ -0,0 +1,98 @@
use std::f64::consts::PI;
use godot::classes::{Marker2D, Node, PathFollow2D, Timer};
use godot::global::{randf, randf_range};
use godot::prelude::*;
use crate::mob::Mob;
use crate::player::Player;
#[derive(GodotClass)]
#[class(init, base=Node)]
pub struct MainScene {
#[export]
mob_scene: OnEditor<Gd<PackedScene>>,
#[var]
score: i64,
// required
base: Base<Node>,
}
#[godot_api]
impl INode for MainScene {
fn ready(&mut self) {
let player = self.base().get_node_as::<Player>("Player");
player.signals().hit().connect_other(self, Self::game_over);
let start_timer = self.base().get_node_as::<Timer>("StartTimer");
let score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
let mob_timer = self.base().get_node_as::<Timer>("MobTimer");
start_timer
.signals()
.timeout()
.connect_other(self, Self::on_start_timer_timeout);
score_timer
.signals()
.timeout()
.connect_other(self, Self::on_score_timer_timeout);
mob_timer
.signals()
.timeout()
.connect_other(self, Self::on_mob_timer_timeout);
self.new_game();
}
}
#[godot_api]
impl MainScene {
fn game_over(&mut self) {
self.base().get_node_as::<Timer>("ScoreTimer").stop();
self.base().get_node_as::<Timer>("MobTimer").stop();
}
fn new_game(&mut self) {
let mut player = self.base().get_node_as::<Player>("Player");
let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
player.bind_mut().start(start_position.get_position());
self.score = 0;
self.base().get_node_as::<Timer>("ScoreTimer").start();
self.base().get_node_as::<Timer>("MobTimer").start();
}
fn on_start_timer_timeout(&mut self) {
self.new_game();
}
fn on_score_timer_timeout(&mut self) {
self.score += 1;
}
fn on_mob_timer_timeout(&mut self) {
let mut mob = self.mob_scene.instantiate_as::<Mob>();
let mut mob_spawn_location = self.base().get_node_as::<PathFollow2D>("MobPath/MobSpawnLocation");
mob_spawn_location.set_progress_ratio(randf() as f32);
let mut direction = mob_spawn_location.get_rotation() as f64 + (PI / 2.0);
direction += randf_range(-PI / 4.0, PI / 4.0);
mob.set_position(mob_spawn_location.get_position());
mob.set_rotation(direction as f32);
let velocity = Vector2::new(randf_range(150.0, 250.0) as f32, 0.0);
mob.set_linear_velocity(velocity.rotated(direction as f32));
self.base_mut().add_child(&mob);
}
}
#[derive(GodotClass)]
#[class(init, base=Node)]
struct CrashFucker {
// required
base: Base<Node>,
}