finish example game (minus ui lol)
This commit is contained in:
parent
e37896ea8e
commit
4c39bd120b
@ -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
64
godot/mob.tscn
Normal 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
33
godot/scene.tscn
Normal 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
24
rust/Cargo.lock
generated
@ -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",
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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
41
rust/src/mob.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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
98
rust/src/scene.rs
Normal 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>,
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user