mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-09 21:08:31 +00:00
Initial commit
This commit is contained in:
commit
720ffc743a
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
client/debug/
|
||||
server/debug/
|
||||
|
||||
|
||||
server/target/
|
||||
client/target/
|
||||
|
||||
|
||||
server/Cargo.lock
|
||||
client/Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
client/**/*.rs.bk
|
||||
server/**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
client/*.pdb
|
||||
server/*.pdb
|
||||
|
||||
|
||||
server/.spacetime
|
||||
|
||||
|
||||
|
||||
# RustRover
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/vcs.xml
|
||||
.idea/modules.xml
|
||||
.idea/.gitignore
|
||||
.idea/horror-game.iml
|
||||
.idea/workspace.xml
|
||||
17
.idea/runConfigurations/PublishServer.xml
generated
Normal file
17
.idea/runConfigurations/PublishServer.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="PublishServer" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
|
||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/publish_server.bat" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="false" />
|
||||
<option name="INTERPRETER_PATH" value="$PROJECT_DIR$/../../../../Windows/System32/WindowsPowerShell/v1.0/powershell.exe" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
20
.idea/runConfigurations/Run_horror_game.xml
generated
Normal file
20
.idea/runConfigurations/Run_horror_game.xml
generated
Normal file
@ -0,0 +1,20 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run horror-game" type="CargoCommandRunConfiguration" factoryName="Cargo Command" singleton="false">
|
||||
<option name="buildProfileId" value="dev" />
|
||||
<option name="command" value="run --package horror-game --bin horror-game" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/client" />
|
||||
<envs />
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Code of Conduct
|
||||
|
||||
This project adheres to the Rust Code of Conduct, which can be found [here](https://www.rust-lang.org/conduct.html).
|
||||
80
CONTRIBUTING.md
Normal file
80
CONTRIBUTING.md
Normal file
@ -0,0 +1,80 @@
|
||||
# Contribution guidelines
|
||||
|
||||
First off, thank you for considering contributing to horror-game.
|
||||
|
||||
If your contribution is not straightforward, please first discuss the change you
|
||||
wish to make by creating a new issue before making the change.
|
||||
|
||||
## Reporting issues
|
||||
|
||||
Before reporting an issue on the
|
||||
[issue tracker](https://github.com/eliasstepanik/horror-game/issues),
|
||||
please check that it has not already been reported by searching for some related
|
||||
keywords.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Try to do one pull request per change.
|
||||
|
||||
### Updating the changelog
|
||||
|
||||
Update the changes you have made in
|
||||
[CHANGELOG](https://github.com/eliasstepanik/horror-game/blob/main/CHANGELOG.md)
|
||||
file under the **Unreleased** section.
|
||||
|
||||
Add the changes of your pull request to one of the following subsections,
|
||||
depending on the types of changes defined by
|
||||
[Keep a changelog](https://keepachangelog.com/en/1.0.0/):
|
||||
|
||||
- `Added` for new features.
|
||||
- `Changed` for changes in existing functionality.
|
||||
- `Deprecated` for soon-to-be removed features.
|
||||
- `Removed` for now removed features.
|
||||
- `Fixed` for any bug fixes.
|
||||
- `Security` in case of vulnerabilities.
|
||||
|
||||
If the required subsection does not exist yet under **Unreleased**, create it!
|
||||
|
||||
## Developing
|
||||
|
||||
### Set up
|
||||
|
||||
This is no different than other Rust projects.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/eliasstepanik/horror-game
|
||||
cd horror-game
|
||||
cargo test
|
||||
```
|
||||
|
||||
### Useful Commands
|
||||
|
||||
- Build and run release version:
|
||||
|
||||
```shell
|
||||
cargo build --release && cargo run --release
|
||||
```
|
||||
|
||||
- Run Clippy:
|
||||
|
||||
```shell
|
||||
cargo clippy --all-targets --all-features --workspace
|
||||
```
|
||||
|
||||
- Run all tests:
|
||||
|
||||
```shell
|
||||
cargo test --all-features --workspace
|
||||
```
|
||||
|
||||
- Check to see if there are code formatting issues
|
||||
|
||||
```shell
|
||||
cargo fmt --all -- --check
|
||||
```
|
||||
|
||||
- Format the code in the project
|
||||
|
||||
```shell
|
||||
cargo fmt --all
|
||||
```
|
||||
176
LICENSE-APACHE.txt
Normal file
176
LICENSE-APACHE.txt
Normal file
@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
21
LICENSE-MIT.txt
Normal file
21
LICENSE-MIT.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Elias Stepanik <eliasstepanik@proton.me>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# horror-game
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0
|
||||
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license
|
||||
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
20
client/Cargo.toml
Normal file
20
client/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "horror-game"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Horror-Game"
|
||||
repository = "https://github.com/eliasstepanik/horror-game"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.15.1", features = ["jpeg", "trace_tracy", "trace_tracy_memory"] }
|
||||
bevy_egui = "0.31.1"
|
||||
bevy_asset = "0.15.0"
|
||||
bevy-inspector-egui = "0.28.0"
|
||||
bevy_reflect = "0.15.0"
|
||||
bevy_render = "0.15.0"
|
||||
bevy_window = "0.15.0"
|
||||
egui_dock = "0.14.0"
|
||||
spacetimedb-sdk = "1.0"
|
||||
hex = "0.4"
|
||||
BIN
client/assets/fonts/minecraft_font.ttf
Normal file
BIN
client/assets/fonts/minecraft_font.ttf
Normal file
Binary file not shown.
73
client/src/app.rs
Normal file
73
client/src/app.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crate::helper::debug_gizmos::debug_gizmos;
|
||||
use crate::helper::egui_dock::{
|
||||
reset_camera_viewport, set_camera_viewport, set_gizmo_mode, show_ui_system, UiState,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_egui::EguiSet;
|
||||
use bevy_render::extract_resource::ExtractResourcePlugin;
|
||||
|
||||
pub struct AppPlugin;
|
||||
|
||||
#[derive(Resource, Debug)]
|
||||
pub struct InspectorVisible(pub bool);
|
||||
|
||||
impl Default for InspectorVisible {
|
||||
fn default() -> Self {
|
||||
InspectorVisible(false)
|
||||
}
|
||||
}
|
||||
impl Plugin for AppPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(UiState::new());
|
||||
app.insert_resource(InspectorVisible(true));
|
||||
|
||||
app.add_plugins(crate::plugins::camera::camera_plugin::CameraPlugin);
|
||||
app.add_plugins(crate::plugins::ui::ui_plugin::UiPlugin);
|
||||
|
||||
app.add_plugins(crate::plugins::environment::environment_plugin::EnvironmentPlugin);
|
||||
|
||||
app.add_systems(Update, (debug_gizmos, toggle_ui_system));
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
show_ui_system
|
||||
.before(EguiSet::ProcessOutput)
|
||||
.before(bevy_egui::systems::end_pass_system)
|
||||
.before(TransformSystem::TransformPropagate)
|
||||
.run_if(should_display_inspector),
|
||||
);
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
(
|
||||
set_camera_viewport
|
||||
.after(show_ui_system)
|
||||
.run_if(should_display_inspector),
|
||||
reset_camera_viewport
|
||||
.run_if(should_not_display_inspector)
|
||||
.after(set_camera_viewport),
|
||||
),
|
||||
);
|
||||
app.add_systems(Update, set_gizmo_mode);
|
||||
app.register_type::<Option<Handle<Image>>>();
|
||||
app.register_type::<AlphaMode>();
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_ui_system(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut inspector_visible: ResMut<InspectorVisible>,
|
||||
) {
|
||||
// =======================
|
||||
// 6) Hide Inspector
|
||||
// =======================
|
||||
if keyboard_input.just_pressed(KeyCode::F1) {
|
||||
inspector_visible.0 = !inspector_visible.0
|
||||
}
|
||||
}
|
||||
|
||||
fn should_display_inspector(inspector_visible: Res<InspectorVisible>) -> bool {
|
||||
inspector_visible.0
|
||||
}
|
||||
|
||||
fn should_not_display_inspector(inspector_visible: Res<InspectorVisible>) -> bool {
|
||||
!inspector_visible.0
|
||||
}
|
||||
18
client/src/helper/debug_gizmos.rs
Normal file
18
client/src/helper/debug_gizmos.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use bevy::color::palettes::css::{BLUE, GREEN, RED};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub fn debug_gizmos(mut gizmos: Gizmos) {
|
||||
|
||||
/* // Draw a line
|
||||
gizmos.line(
|
||||
Vec3::ZERO,
|
||||
Vec3::new(1.0, 1.0, 1.0),
|
||||
RED,
|
||||
);
|
||||
|
||||
// Draw a sphere
|
||||
gizmos.sphere(Vec3::new(2.0, 2.0, 2.0), 0.5, BLUE);
|
||||
|
||||
// Draw a wireframe cube
|
||||
gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);*/
|
||||
}
|
||||
342
client/src/helper/egui_dock.rs
Normal file
342
client/src/helper/egui_dock.rs
Normal file
@ -0,0 +1,342 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_asset::{ReflectAsset, UntypedAssetId};
|
||||
use bevy_egui::{egui, EguiContext};
|
||||
use bevy_inspector_egui::bevy_inspector::hierarchy::{hierarchy_ui, SelectedEntities};
|
||||
use bevy_inspector_egui::bevy_inspector::{
|
||||
self, ui_for_entities_shared_components, ui_for_entity_with_children,
|
||||
};
|
||||
use bevy_reflect::TypeRegistry;
|
||||
use bevy_render::camera::{CameraProjection, Viewport};
|
||||
use bevy_window::{PrimaryWindow, Window};
|
||||
use egui_dock::{DockArea, DockState, NodeIndex, Style};
|
||||
use std::any::TypeId;
|
||||
|
||||
#[cfg(egui_dock_gizmo)]
|
||||
use transform_gizmo_egui::GizmoMode;
|
||||
|
||||
/// Placeholder type if gizmo is disabled.
|
||||
#[cfg(not(egui_dock_gizmo))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GizmoMode;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MainCamera;
|
||||
|
||||
pub fn show_ui_system(world: &mut World) {
|
||||
let Ok(egui_context) = world
|
||||
.query_filtered::<&mut EguiContext, With<PrimaryWindow>>()
|
||||
.get_single(world)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut egui_context = egui_context.clone();
|
||||
|
||||
world.resource_scope::<UiState, _>(|world, mut ui_state| {
|
||||
ui_state.ui(world, egui_context.get_mut())
|
||||
});
|
||||
}
|
||||
|
||||
// make camera only render to view not obpub structed by UI
|
||||
pub fn set_camera_viewport(
|
||||
ui_state: Res<UiState>,
|
||||
primary_window: Query<&mut Window, With<PrimaryWindow>>,
|
||||
egui_settings: Query<&bevy_egui::EguiSettings>,
|
||||
mut cameras: Query<&mut Camera, With<MainCamera>>,
|
||||
) {
|
||||
let mut cam = cameras.single_mut();
|
||||
|
||||
let Ok(window) = primary_window.get_single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let scale_factor = window.scale_factor() * egui_settings.single().scale_factor;
|
||||
|
||||
let viewport_pos = ui_state.viewport_rect.left_top().to_vec2() * scale_factor;
|
||||
let viewport_size = ui_state.viewport_rect.size() * scale_factor;
|
||||
|
||||
let physical_position = UVec2::new(viewport_pos.x as u32, viewport_pos.y as u32);
|
||||
let physical_size = UVec2::new(viewport_size.x as u32, viewport_size.y as u32);
|
||||
|
||||
// The desired viewport rectangle at its offset in "physical pixel space"
|
||||
let rect = physical_position + physical_size;
|
||||
|
||||
let window_size = window.physical_size();
|
||||
// wgpu will panic if trying to set a viewport rect which has coordinates extending
|
||||
// past the size of the render target, i.e. the physical window in our case.
|
||||
// Typically this shouldn't happen- but during init and resizing etc. edge cases might occur.
|
||||
// Simply do nothing in those cases.
|
||||
if rect.x <= window_size.x && rect.y <= window_size.y {
|
||||
cam.viewport = Some(Viewport {
|
||||
physical_position,
|
||||
physical_size,
|
||||
depth: 0.0..1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_camera_viewport(mut cameras: Query<&mut Camera, With<MainCamera>>) {
|
||||
if let Ok(mut cam) = cameras.get_single_mut() {
|
||||
cam.viewport = None; // Reset the viewport to its default state
|
||||
}
|
||||
}
|
||||
pub fn set_gizmo_mode(input: Res<ButtonInput<KeyCode>>, mut ui_state: ResMut<UiState>) {
|
||||
#[cfg(egui_dock_gizmo)]
|
||||
let keybinds = [
|
||||
(KeyCode::KeyR, GizmoMode::Rotate),
|
||||
(KeyCode::KeyT, GizmoMode::Translate),
|
||||
(KeyCode::KeyS, GizmoMode::Scale),
|
||||
];
|
||||
#[cfg(not(egui_dock_gizmo))]
|
||||
let keybinds = [];
|
||||
for (key, mode) in keybinds {
|
||||
if input.just_pressed(key) {
|
||||
ui_state.gizmo_mode = mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum InspectorSelection {
|
||||
Entities,
|
||||
Resource(TypeId, String),
|
||||
Asset(TypeId, String, UntypedAssetId),
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct UiState {
|
||||
state: DockState<EguiWindow>,
|
||||
viewport_rect: egui::Rect,
|
||||
selected_entities: SelectedEntities,
|
||||
selection: InspectorSelection,
|
||||
gizmo_mode: GizmoMode,
|
||||
}
|
||||
|
||||
impl UiState {
|
||||
pub fn new() -> Self {
|
||||
let mut state = DockState::new(vec![EguiWindow::GameView]);
|
||||
let tree = state.main_surface_mut();
|
||||
let [game, _inspector] =
|
||||
tree.split_right(NodeIndex::root(), 0.75, vec![EguiWindow::Inspector]);
|
||||
let [game, _hierarchy] = tree.split_left(game, 0.2, vec![EguiWindow::Hierarchy]);
|
||||
let [_game, _bottom] =
|
||||
tree.split_below(game, 0.8, vec![EguiWindow::Resources, EguiWindow::Assets]);
|
||||
|
||||
Self {
|
||||
state,
|
||||
selected_entities: SelectedEntities::default(),
|
||||
selection: InspectorSelection::Entities,
|
||||
viewport_rect: egui::Rect::NOTHING,
|
||||
#[cfg(egui_dock_gizmo)]
|
||||
gizmo_mode: GizmoMode::Translate,
|
||||
#[cfg(not(egui_dock_gizmo))]
|
||||
gizmo_mode: GizmoMode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, world: &mut World, ctx: &mut egui::Context) {
|
||||
let mut tab_viewer = TabViewer {
|
||||
world,
|
||||
viewport_rect: &mut self.viewport_rect,
|
||||
selected_entities: &mut self.selected_entities,
|
||||
selection: &mut self.selection,
|
||||
gizmo_mode: self.gizmo_mode,
|
||||
};
|
||||
DockArea::new(&mut self.state)
|
||||
.style(Style::from_egui(ctx.style().as_ref()))
|
||||
.show(ctx, &mut tab_viewer);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EguiWindow {
|
||||
GameView,
|
||||
Hierarchy,
|
||||
Resources,
|
||||
Assets,
|
||||
Inspector,
|
||||
}
|
||||
|
||||
pub struct TabViewer<'a> {
|
||||
world: &'a mut World,
|
||||
selected_entities: &'a mut SelectedEntities,
|
||||
selection: &'a mut InspectorSelection,
|
||||
viewport_rect: &'a mut egui::Rect,
|
||||
gizmo_mode: GizmoMode,
|
||||
}
|
||||
|
||||
impl egui_dock::TabViewer for TabViewer<'_> {
|
||||
type Tab = EguiWindow;
|
||||
|
||||
fn ui(&mut self, ui: &mut egui_dock::egui::Ui, window: &mut Self::Tab) {
|
||||
let type_registry = self.world.resource::<AppTypeRegistry>().0.clone();
|
||||
let type_registry = type_registry.read();
|
||||
|
||||
match window {
|
||||
EguiWindow::GameView => {
|
||||
*self.viewport_rect = ui.clip_rect();
|
||||
|
||||
draw_gizmo(ui, self.world, self.selected_entities, self.gizmo_mode);
|
||||
}
|
||||
EguiWindow::Hierarchy => {
|
||||
let selected = hierarchy_ui(self.world, ui, self.selected_entities);
|
||||
if selected {
|
||||
*self.selection = InspectorSelection::Entities;
|
||||
}
|
||||
}
|
||||
EguiWindow::Resources => select_resource(ui, &type_registry, self.selection),
|
||||
EguiWindow::Assets => select_asset(ui, &type_registry, self.world, self.selection),
|
||||
EguiWindow::Inspector => match *self.selection {
|
||||
InspectorSelection::Entities => match self.selected_entities.as_slice() {
|
||||
&[entity] => ui_for_entity_with_children(self.world, entity, ui),
|
||||
entities => ui_for_entities_shared_components(self.world, entities, ui),
|
||||
},
|
||||
InspectorSelection::Resource(type_id, ref name) => {
|
||||
ui.label(name);
|
||||
bevy_inspector::by_type_id::ui_for_resource(
|
||||
self.world,
|
||||
type_id,
|
||||
ui,
|
||||
name,
|
||||
&type_registry,
|
||||
)
|
||||
}
|
||||
InspectorSelection::Asset(type_id, ref name, handle) => {
|
||||
ui.label(name);
|
||||
bevy_inspector::by_type_id::ui_for_asset(
|
||||
self.world,
|
||||
type_id,
|
||||
handle,
|
||||
ui,
|
||||
&type_registry,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn title(&mut self, window: &mut Self::Tab) -> egui_dock::egui::WidgetText {
|
||||
format!("{window:?}").into()
|
||||
}
|
||||
|
||||
fn clear_background(&self, window: &Self::Tab) -> bool {
|
||||
!matches!(window, EguiWindow::GameView)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn draw_gizmo(
|
||||
ui: &mut egui::Ui,
|
||||
world: &mut World,
|
||||
selected_entities: &SelectedEntities,
|
||||
gizmo_mode: GizmoMode,
|
||||
) {
|
||||
let (cam_transform, projection) = world
|
||||
.query_filtered::<(&GlobalTransform, &Projection), With<MainCamera>>()
|
||||
.single(world);
|
||||
let view_matrix = Mat4::from(cam_transform.affine().inverse());
|
||||
let projection_matrix = projection.get_clip_from_view();
|
||||
|
||||
if selected_entities.len() != 1 {
|
||||
#[allow(clippy::needless_return)]
|
||||
return;
|
||||
}
|
||||
|
||||
/*for selected in selected_entities.iter() {
|
||||
let Some(transform) = world.get::<Transform>(selected) else {
|
||||
continue;
|
||||
};
|
||||
let model_matrix = transform.compute_matrix();
|
||||
|
||||
let mut gizmo = Gizmo::new(GizmoConfig {
|
||||
view_matrix: view_matrix.into(),
|
||||
projection_matrix: projection_matrix.into(),
|
||||
orientation: GizmoOrientation::Local,
|
||||
modes: EnumSet::from(gizmo_mode),
|
||||
..Default::default()
|
||||
});
|
||||
let Some([result]) = gizmo
|
||||
.interact(ui, model_matrix.into())
|
||||
.map(|(_, res)| res.as_slice())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut transform = world.get_mut::<Transform>(selected).unwrap();
|
||||
transform = Transform {
|
||||
translation: Vec3::from(<[f64; 3]>::from(result.translation)),
|
||||
rotation: Quat::from_array(<[f64; 4]>::from(result.rotation)),
|
||||
scale: Vec3::from(<[f64; 3]>::from(result.scale)),
|
||||
};
|
||||
}*/
|
||||
}
|
||||
|
||||
pub fn select_resource(
|
||||
ui: &mut egui::Ui,
|
||||
type_registry: &TypeRegistry,
|
||||
selection: &mut InspectorSelection,
|
||||
) {
|
||||
let mut resources: Vec<_> = type_registry
|
||||
.iter()
|
||||
.filter(|registration| registration.data::<ReflectResource>().is_some())
|
||||
.map(|registration| {
|
||||
(
|
||||
registration.type_info().type_path_table().short_path(),
|
||||
registration.type_id(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
resources.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b));
|
||||
|
||||
for (resource_name, type_id) in resources {
|
||||
let selected = match *selection {
|
||||
InspectorSelection::Resource(selected, _) => selected == type_id,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if ui.selectable_label(selected, resource_name).clicked() {
|
||||
*selection = InspectorSelection::Resource(type_id, resource_name.to_string());
|
||||
}
|
||||
debug!("{}", resource_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_asset(
|
||||
ui: &mut egui::Ui,
|
||||
type_registry: &TypeRegistry,
|
||||
world: &World,
|
||||
selection: &mut InspectorSelection,
|
||||
) {
|
||||
let mut assets: Vec<_> = type_registry
|
||||
.iter()
|
||||
.filter_map(|registration| {
|
||||
let reflect_asset = registration.data::<ReflectAsset>()?;
|
||||
Some((
|
||||
registration.type_info().type_path_table().short_path(),
|
||||
registration.type_id(),
|
||||
reflect_asset,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
assets.sort_by(|(name_a, ..), (name_b, ..)| name_a.cmp(name_b));
|
||||
|
||||
for (asset_name, asset_type_id, reflect_asset) in assets {
|
||||
let handles: Vec<_> = reflect_asset.ids(world).collect();
|
||||
|
||||
ui.collapsing(format!("{asset_name} ({})", handles.len()), |ui| {
|
||||
for handle in handles {
|
||||
let selected = match *selection {
|
||||
InspectorSelection::Asset(_, _, selected_id) => selected_id == handle,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if ui
|
||||
.selectable_label(selected, format!("{:?}", handle))
|
||||
.clicked()
|
||||
{
|
||||
*selection =
|
||||
InspectorSelection::Asset(asset_type_id, asset_name.to_string(), handle);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
2
client/src/helper/mod.rs
Normal file
2
client/src/helper/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod debug_gizmos;
|
||||
pub mod egui_dock;
|
||||
84
client/src/main.rs
Normal file
84
client/src/main.rs
Normal file
@ -0,0 +1,84 @@
|
||||
mod app;
|
||||
mod helper;
|
||||
mod plugins;
|
||||
use crate::app::AppPlugin;
|
||||
use bevy::gizmos::{AppGizmoBuilder, GizmoPlugin};
|
||||
use bevy::log::info;
|
||||
use bevy::prelude::{default, App, GizmoConfigGroup, PluginGroup, Reflect, Res, Resource};
|
||||
use bevy::render::settings::{Backends, RenderCreation, WgpuSettings};
|
||||
use bevy::render::RenderPlugin;
|
||||
use bevy::DefaultPlugins;
|
||||
use bevy_egui::EguiPlugin;
|
||||
use bevy_inspector_egui::DefaultInspectorConfigPlugin;
|
||||
use bevy_window::{PresentMode, Window, WindowPlugin};
|
||||
|
||||
const TITLE: &str = "horror-game";
|
||||
const RESOLUTION: (f32, f32) = (1920f32, 1080f32);
|
||||
const RESIZABLE: bool = true;
|
||||
const DECORATIONS: bool = true;
|
||||
const TRANSPARENT: bool = true;
|
||||
const PRESENT_MODE: PresentMode = PresentMode::AutoVsync;
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
register_platform_plugins(&mut app);
|
||||
|
||||
app.add_plugins(AppPlugin);
|
||||
app.add_plugins(EguiPlugin);
|
||||
app.add_plugins(DefaultInspectorConfigPlugin);
|
||||
/*app.add_plugins(GizmoPlugin);*/
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct InspectorVisible(bool);
|
||||
fn register_platform_plugins(app: &mut App) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Register Windows-specific plugins
|
||||
info!("Adding Windows-specific plugins");
|
||||
app.add_plugins(
|
||||
DefaultPlugins
|
||||
.set(RenderPlugin {
|
||||
render_creation: RenderCreation::Automatic(WgpuSettings {
|
||||
backends: Some(Backends::VULKAN),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
})
|
||||
.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: TITLE.to_string(), // Window title
|
||||
resolution: RESOLUTION.into(), // Initial resolution (width x height)
|
||||
resizable: RESIZABLE, // Allow resizing
|
||||
decorations: DECORATIONS, // Enable window decorations
|
||||
transparent: TRANSPARENT, // Opaque background
|
||||
present_mode: PRESENT_MODE, // VSync mode
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
info!("Adding macOS-specific plugins");
|
||||
app.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: crate::TITLE.to_string(), // Window title
|
||||
resolution: crate::RESOLUTION.into(), // Initial resolution (width x height)
|
||||
resizable: crate::RESIZABLE, // Allow resizing
|
||||
decorations: crate::DECORATIONS, // Enable window decorations
|
||||
transparent: crate::TRANSPARENT, // Opaque background
|
||||
present_mode: crate::PRESENT_MODE, // VSync mode
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}));
|
||||
}
|
||||
}
|
||||
fn should_display_inspector(inspector_visible: Res<InspectorVisible>) -> bool {
|
||||
inspector_visible.0
|
||||
}
|
||||
16
client/src/plugins/camera/camera_plugin.rs
Normal file
16
client/src/plugins/camera/camera_plugin.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use bevy::a11y::AccessibilitySystem::Update;
|
||||
use bevy::app::{App, Plugin, PreUpdate, Startup};
|
||||
|
||||
pub struct CameraPlugin;
|
||||
impl Plugin for CameraPlugin {
|
||||
fn build(&self, _app: &mut App) {
|
||||
_app.add_systems(
|
||||
Startup,
|
||||
(crate::plugins::camera::systems::camera_system::setup),
|
||||
);
|
||||
_app.add_systems(
|
||||
PreUpdate,
|
||||
(crate::plugins::camera::systems::camera_system::camera_controller_system),
|
||||
);
|
||||
}
|
||||
}
|
||||
2
client/src/plugins/camera/mod.rs
Normal file
2
client/src/plugins/camera/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod camera_plugin;
|
||||
pub mod systems;
|
||||
156
client/src/plugins/camera/systems/camera_system.rs
Normal file
156
client/src/plugins/camera/systems/camera_system.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use crate::helper::egui_dock::MainCamera;
|
||||
use bevy::input::mouse::{MouseMotion, MouseWheel};
|
||||
use bevy::math::Vec3;
|
||||
use bevy::prelude::*;
|
||||
use bevy_render::camera::{Exposure, PhysicalCameraParameters, Projection};
|
||||
use bevy_window::CursorGrabMode;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CameraController {
|
||||
pub yaw: f32,
|
||||
pub pitch: f32,
|
||||
pub speed: f32,
|
||||
pub sensitivity: f32,
|
||||
}
|
||||
|
||||
impl Default for CameraController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
speed: 10.0,
|
||||
sensitivity: 0.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
Transform::from_xyz(0.0, 0.0, 10.0), // initial f32
|
||||
GlobalTransform::default(),
|
||||
Camera3d::default(),
|
||||
Projection::from(PerspectiveProjection {
|
||||
near: 0.0001,
|
||||
..default()
|
||||
}),
|
||||
MainCamera,
|
||||
CameraController::default(),
|
||||
Exposure::from_physical_camera(PhysicalCameraParameters {
|
||||
aperture_f_stops: 1.0,
|
||||
shutter_speed_s: 1.0 / 125.0,
|
||||
sensitivity_iso: 100.0,
|
||||
sensor_height: 0.01866,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
/// Example system to control a camera using double-precision for position.
|
||||
pub fn camera_controller_system(
|
||||
time: Res<Time>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>, /*
|
||||
mouse_button_input: Res<ButtonInput<MouseButton>>,*/
|
||||
mut mouse_motion_events: EventReader<MouseMotion>,
|
||||
mut mouse_wheel_events: EventReader<MouseWheel>,
|
||||
mut windows: Query<&mut Window>,
|
||||
mut query: Query<(&mut Transform, &mut CameraController)>,
|
||||
mut app_exit_events: EventWriter<AppExit>,
|
||||
) {
|
||||
let mut window = windows.single_mut();
|
||||
let (mut transform, mut controller) = query.single_mut();
|
||||
|
||||
// ====================
|
||||
// 1) Handle Mouse Look
|
||||
// ====================
|
||||
if !window.cursor_options.visible {
|
||||
for event in mouse_motion_events.read() {
|
||||
// Adjust yaw/pitch in f32
|
||||
controller.yaw -= event.delta.x * controller.sensitivity;
|
||||
controller.pitch += event.delta.y * controller.sensitivity;
|
||||
controller.pitch = controller.pitch.clamp(-89.9, 89.9);
|
||||
|
||||
// Convert degrees to radians (f32)
|
||||
let yaw_radians = controller.yaw.to_radians();
|
||||
let pitch_radians = controller.pitch.to_radians();
|
||||
|
||||
// Build a double-precision quaternion from those angles
|
||||
let rot_yaw = Quat::from_axis_angle(Vec3::Y, yaw_radians);
|
||||
let rot_pitch = Quat::from_axis_angle(Vec3::X, -pitch_radians);
|
||||
|
||||
transform.rotation = rot_yaw * rot_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 2) Adjust Movement Speed with Mouse Wheel
|
||||
// ====================
|
||||
for event in mouse_wheel_events.read() {
|
||||
let base_factor = 1.1_f32;
|
||||
let factor = base_factor.powf(event.y);
|
||||
controller.speed *= factor;
|
||||
if controller.speed < 0.01 {
|
||||
controller.speed = 0.01;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// 3) Handle Keyboard Movement (WASD, Space, Shift)
|
||||
// ====================
|
||||
let mut direction = Vec3::ZERO;
|
||||
|
||||
// Forward/Back
|
||||
if keyboard_input.pressed(KeyCode::KeyW) {
|
||||
direction += transform.forward().as_vec3();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyS) {
|
||||
direction -= transform.forward().as_vec3();
|
||||
}
|
||||
|
||||
// Left/Right
|
||||
if keyboard_input.pressed(KeyCode::KeyA) {
|
||||
direction -= transform.right().as_vec3();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyD) {
|
||||
direction += transform.right().as_vec3();
|
||||
}
|
||||
|
||||
// Up/Down
|
||||
if keyboard_input.pressed(KeyCode::Space) {
|
||||
direction += transform.up().as_vec3();
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::ShiftLeft) || keyboard_input.pressed(KeyCode::ShiftRight) {
|
||||
direction -= transform.up().as_vec3();
|
||||
}
|
||||
|
||||
// Normalize direction if needed
|
||||
if direction.length_squared() > 0.0 {
|
||||
direction = direction.normalize();
|
||||
}
|
||||
|
||||
// Apply movement in double-precision
|
||||
let delta_seconds = time.delta_secs_f64();
|
||||
let distance = controller.speed as f64 * delta_seconds;
|
||||
transform.translation += direction * distance as f32;
|
||||
|
||||
// =========================
|
||||
// 4) Lock/Unlock Mouse (L)
|
||||
// =========================
|
||||
if keyboard_input.just_pressed(KeyCode::KeyL) {
|
||||
// Toggle between locked and unlocked
|
||||
if window.cursor_options.grab_mode == CursorGrabMode::None {
|
||||
// Lock
|
||||
window.cursor_options.visible = false;
|
||||
window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||
} else {
|
||||
// Unlock
|
||||
window.cursor_options.visible = true;
|
||||
window.cursor_options.grab_mode = CursorGrabMode::None;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================
|
||||
// 7) Exit on Escape
|
||||
// =======================
|
||||
if keyboard_input.pressed(KeyCode::Escape) {
|
||||
app_exit_events.send(Default::default());
|
||||
}
|
||||
}
|
||||
1
client/src/plugins/camera/systems/mod.rs
Normal file
1
client/src/plugins/camera/systems/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod camera_system;
|
||||
9
client/src/plugins/environment/environment_plugin.rs
Normal file
9
client/src/plugins/environment/environment_plugin.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use bevy::app::{App, Plugin, Startup};
|
||||
use bevy::color::palettes::basic::{GREEN, YELLOW};
|
||||
use bevy::color::palettes::css::RED;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct EnvironmentPlugin;
|
||||
impl Plugin for EnvironmentPlugin {
|
||||
fn build(&self, app: &mut App) {}
|
||||
}
|
||||
2
client/src/plugins/environment/mod.rs
Normal file
2
client/src/plugins/environment/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod environment_plugin;
|
||||
pub mod systems;
|
||||
10
client/src/plugins/environment/systems/environment_system.rs
Normal file
10
client/src/plugins/environment/systems/environment_system.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use bevy::color::palettes::basic::*;
|
||||
use bevy::color::palettes::css::{BEIGE, MIDNIGHT_BLUE, ORANGE, ORANGE_RED, SEA_GREEN};
|
||||
use bevy::math::*;
|
||||
use bevy::pbr::{CascadeShadowConfigBuilder, NotShadowCaster};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub fn setup(mut commands: Commands) {
|
||||
|
||||
// Insert the octree into the ECS
|
||||
}
|
||||
1
client/src/plugins/environment/systems/mod.rs
Normal file
1
client/src/plugins/environment/systems/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod environment_system;
|
||||
3
client/src/plugins/mod.rs
Normal file
3
client/src/plugins/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod camera;
|
||||
pub mod environment;
|
||||
pub mod ui;
|
||||
2
client/src/plugins/ui/mod.rs
Normal file
2
client/src/plugins/ui/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod systems;
|
||||
pub mod ui_plugin;
|
||||
1
client/src/plugins/ui/systems/mod.rs
Normal file
1
client/src/plugins/ui/systems/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod ui_system;
|
||||
62
client/src/plugins/ui/systems/ui_system.rs
Normal file
62
client/src/plugins/ui/systems/ui_system.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use crate::plugins::camera::systems::camera_system::CameraController;
|
||||
use bevy::asset::AssetServer;
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SpeedDisplay;
|
||||
|
||||
/// Spawns a UI Text entity to display speed/positions.
|
||||
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
// Use the new UI API, or the old UI Node-based system.
|
||||
// This example uses an older approach to Node/Style, but can be adapted to `TextBundle`.
|
||||
// If you're on Bevy 0.11+, you can also do `TextBundle::from_section(...)`.
|
||||
commands.spawn((
|
||||
// The text to display:
|
||||
Text::new("Speed: 0.0"),
|
||||
// The font, loaded from an asset file
|
||||
TextFont {
|
||||
font: asset_server.load("fonts/minecraft_font.ttf"),
|
||||
font_size: 25.0,
|
||||
..default()
|
||||
},
|
||||
// The text layout style
|
||||
TextLayout::new_with_justify(JustifyText::Left),
|
||||
// Style for positioning the UI node
|
||||
Node {
|
||||
position_type: PositionType::Relative,
|
||||
bottom: Val::Px(9.0),
|
||||
right: Val::Px(9.0),
|
||||
..default()
|
||||
},
|
||||
// Our marker so we can query this entity
|
||||
SpeedDisplay,
|
||||
));
|
||||
}
|
||||
|
||||
/// System that updates the UI text each frame with
|
||||
/// - speed
|
||||
/// - camera f32 position
|
||||
/// - camera global f64 position
|
||||
/// - current chunk coordinate
|
||||
pub fn update(
|
||||
// Query the camera controller so we can see its speed
|
||||
query_camera_controller: Query<&CameraController>,
|
||||
// We also query for the camera's f32 `Transform` and the double `DoubleTransform`
|
||||
camera_query: Query<(&Transform, &Camera)>,
|
||||
|
||||
// The UI text entity
|
||||
mut query_text: Query<&mut Text, With<SpeedDisplay>>,
|
||||
) {
|
||||
let camera_controller = query_camera_controller.single();
|
||||
let (transform, _camera) = camera_query.single();
|
||||
let mut text = query_text.single_mut();
|
||||
|
||||
// Format the string to show speed, positions, and chunk coords
|
||||
text.0 = format!(
|
||||
"\n Speed: {:.3}\n Position(f32): ({:.2},{:.2},{:.2})",
|
||||
camera_controller.speed,
|
||||
transform.translation.x,
|
||||
transform.translation.y,
|
||||
transform.translation.z,
|
||||
);
|
||||
}
|
||||
11
client/src/plugins/ui/ui_plugin.rs
Normal file
11
client/src/plugins/ui/ui_plugin.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use crate::plugins::ui::systems::ui_system::*;
|
||||
use bevy::app::{App, FixedUpdate, Plugin, PreUpdate, Startup};
|
||||
use bevy::prelude::IntoSystemConfigs;
|
||||
|
||||
pub struct UiPlugin;
|
||||
impl Plugin for UiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(FixedUpdate, update);
|
||||
}
|
||||
}
|
||||
5
publish_server.bat
Normal file
5
publish_server.bat
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
@echo off
|
||||
REM Script to publish the horror-game project using spacetime
|
||||
|
||||
spacetime publish -c --project-path server horror-game
|
||||
13
server/Cargo.toml
Normal file
13
server/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "spacetime-module"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
spacetimedb = "1.0.1"
|
||||
log = "0.4"
|
||||
34
server/src/lib.rs
Normal file
34
server/src/lib.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use spacetimedb::{ReducerContext, Table};
|
||||
|
||||
#[spacetimedb::table(name = person)]
|
||||
pub struct Person {
|
||||
name: String
|
||||
}
|
||||
|
||||
#[spacetimedb::reducer(init)]
|
||||
pub fn init(_ctx: &ReducerContext) {
|
||||
// Called when the module is initially published
|
||||
}
|
||||
|
||||
#[spacetimedb::reducer(client_connected)]
|
||||
pub fn identity_connected(_ctx: &ReducerContext) {
|
||||
// Called everytime a new client connects
|
||||
}
|
||||
|
||||
#[spacetimedb::reducer(client_disconnected)]
|
||||
pub fn identity_disconnected(_ctx: &ReducerContext) {
|
||||
// Called everytime a client disconnects
|
||||
}
|
||||
|
||||
#[spacetimedb::reducer]
|
||||
pub fn add(ctx: &ReducerContext, name: String) {
|
||||
ctx.db.person().insert(Person { name });
|
||||
}
|
||||
|
||||
#[spacetimedb::reducer]
|
||||
pub fn say_hello(ctx: &ReducerContext) {
|
||||
for person in ctx.db.person().iter() {
|
||||
log::info!("Hello, {}!", person.name);
|
||||
}
|
||||
log::info!("Hello, World!");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user