-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
2,605 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
# Unhaunter | ||
|
||
A paranormal investigation game in an isometric perspective. | ||
|
||
## How to Play | ||
|
||
Your task is to expel the ghost(s) from a location (currently 1 ghost only). | ||
|
||
To be able to do this, first you need to identify the ghost among 44 different | ||
possible ghost types. Each ghost type interacts with your equipment in a | ||
different way. Each piece of equipment is responsible for detecting a type of | ||
evidence. | ||
|
||
There are 8 types of evidence. A ghost has 5 evidences out of those 8. | ||
|
||
Locate the ghost, test the different equipment, note down in the van which | ||
evidences you found, create the "Unhaunter Ghost Repellent" and use it to expel | ||
the ghost. | ||
|
||
Once you're done, you can click "End Mission" on the van and you'll get the | ||
mission score. | ||
|
||
Press [E] on the van to access the van UI for the journal and other useful | ||
stuff. The same key is used to open doors or actuate switches and lamps. | ||
|
||
Press [R] to activate the gear on your right hand, [T] for the gear in the left | ||
hand. | ||
|
||
Press [Q] to cycle the inventory on your right hand. [TAB] to swap your left | ||
and right hands. | ||
|
||
## Evidences | ||
|
||
* Freezing temps: Thermometer. The room that the ghost frequents can go below | ||
zero. Be warned that lights heat up the room and open doors will leak air | ||
outside. These factors limit your ability to read freezing temps. | ||
|
||
* Floating Orbs: Night Vision IR Camera. The ghost's breach (grey translucent | ||
dust) can illuminate under Night Vision (IR). The room has to be dark for this | ||
effect to be perceptible. | ||
|
||
* UV Ectoplasm: UV Torch. The ghost might glow green under UV light. Other light | ||
sources might make this very hard to see. | ||
|
||
* EMF Level 5: EMF Meter. The EMF Meter might read EMF5 for some ghosts. | ||
|
||
* EVP Recording: Recorder. The recorder might show up a "EVP Recorded" message. | ||
|
||
* Spirit Box: Spirit Box. Screams and other paranormal sounds might be heard | ||
through the static. | ||
|
||
* RL Presence: Red Light Torch. The ghost might glow orange under this light. | ||
Other light sources might need to be off for the effect to be evident. | ||
|
||
* 500+ cpm: Geiger Counter. The device might read above 500cpm for some ghosts. | ||
|
||
## Basic Strategy | ||
|
||
First, locate where the ghost is - what room it tends to roam. For this, it is | ||
best to turn on all the lights in the house and open all doors. | ||
|
||
Press [T] to enable the torch; it has several power settings, but be warned | ||
that it might turn itself off by overheating. | ||
|
||
The ghost has a spawn point, which also sets its favorite room. This spawn point | ||
is known as the ghost's breach, which can be seen as a form of white, | ||
semi-transparent dust. This is better seen using the location's lights; a | ||
regular torch will not show it. | ||
|
||
The ghost always roams around the breach. So most of the analysis and tests | ||
should be done in this room. | ||
|
||
Once this is located, turn off the lights of that room and the rooms contiguous | ||
to it. Also, close the doors of the room. | ||
|
||
Try the different equipment and note which ones gave positive results. | ||
|
||
Go to the van to note these down in the journal. It is not possible to note | ||
these down outside of the van, so if you cannot memorize them, you'll need to | ||
make more trips to the van to write them down. | ||
|
||
As you set these on the van, the list of possible ghosts will narrow. Once | ||
you're sure which one it is, select it and you'll be able to click | ||
"Craft Unhaunter Ghost Repellent". | ||
|
||
This will fill a vial in your inventory with the repellent for that particular | ||
ghost type. Go to the ghost room (breach) and wait for the ghost to be there, | ||
then activate the vial, which will spread the substance. | ||
|
||
If successful, the ghost should disappear. Be warned, there's no cue indicating | ||
if it worked. You need to double-check that the ghost is gone. | ||
If needed, you can refill the vial in the van. | ||
|
||
Once you're sure there are no more ghosts, proceed to the van | ||
and click "End Mission". | ||
|
||
## Building and Installing | ||
|
||
There are no binary, packages or installers for Unhaunter. So far there are no | ||
plans for them, the only way to run the game is to build it from sources. | ||
|
||
Download the repository as usual via your favorite method, for example: | ||
|
||
$ git clone https://github.com/deavid/unhaunter.git | ||
|
||
You'll need to have Rust, follow the steps to install it at: | ||
|
||
https://www.rust-lang.org/tools/install | ||
|
||
You'll need also to install dependencies for Bevy, follow the instructions for | ||
your operating system at: | ||
|
||
https://bevyengine.org/learn/quick-start/getting-started/setup/#installing-os-dependencies | ||
|
||
With this, you should be able to run the game by simply running: | ||
|
||
$ cargo run | ||
|
||
The command must be run from the game source folder. | ||
|
||
NOTE: The game is currently being developed and built on a single developer | ||
machine using Debian GNU/Linux in some Testing/Sid version in an AMD machine | ||
using a NVIDIA 3080. The game should run in a wide range of computers and | ||
configurations, but it has not been tested. | ||
|
||
## Profiling | ||
|
||
Unhaunter, as any other game, will perform wildly different depending on where | ||
it is executed. If there are performance issues on your system, you can help | ||
by profiling the problem yourself. | ||
|
||
WARN: Profiling creates gigabytes worth of data. It is imperative that you know | ||
what do you want to test and do it as quickly as possible. A minute worth of | ||
data could be over 3 GiB. | ||
|
||
To run a profiling session, run: | ||
|
||
$ cargo run --release --features bevy/trace_chrome | ||
|
||
This will create a file named `trace-1999999999999999.json` in the same folder | ||
from where you executed `cargo run` (numbers will be different). | ||
|
||
Be warned that the trace might also contain some private information about your | ||
system. The trace can be opened by others, but only send it to trusted people. | ||
(It shows the paths of where do you have Bevy and other libraries installed, | ||
which is just a minor concern) | ||
|
||
Being a JSON file, it is likely that it can be compressed really well. You can | ||
use 7-Zip, or other tools. ZSTD, if you have it, will probably yield good | ||
results in a short amount of time. | ||
|
||
For example, compressing a sample trace allows us to get 1.3 GiB | ||
compressed into 33 MiB: | ||
|
||
``` | ||
$ zstd -9kv trace-1709882754518652.json | ||
*** Zstandard CLI (64-bit) v1.5.4, by Yann Collet *** | ||
trace-1709882754518652.json : 2.52% ( 1.30 GiB => 33.5 MiB, trace-1709882754518652.json.zst) | ||
``` | ||
|
||
This could be useful to share via email or other methods. ZSTD gives very good | ||
compression ratios at quite fast speed. | ||
|
||
If you want to inspect the file yourself, you can use https://ui.perfetto.dev | ||
|
||
But usually it won't fit in the browser WASM limit (2 GiB), so you might need | ||
to follow instructions here: | ||
|
||
https://perfetto.dev/docs/quickstart/trace-analysis#trace-processor | ||
|
||
Sample session: | ||
|
||
``` | ||
$ curl -LO https://get.perfetto.dev/trace_processor | ||
% Total % Received % Xferd Average Speed Time Time Time Current | ||
Dload Upload Total Spent Left Speed | ||
100 9759 100 9759 0 0 10107 0 --:--:-- --:--:-- --:--:-- 10102 | ||
$ chmod +x ./trace_processor | ||
$ ./trace_processor ~/git/rust/unhaunter/trace-1709880857003805.json --httpd | ||
[865.803] processor_shell.cc:1636 Trace loaded: 2644.45 MB in 63.29s (41.8 MB/s) | ||
[865.804] httpd.cc:99 [HTTP] Starting RPC server on localhost:9001 | ||
[865.804] httpd.cc:104 [HTTP] This server can be used by reloading https://ui.perfetto.dev and clicking on YES on the "Trace Processor native acceleration" dialog or through the Python API (see https://perfetto.dev/docs/analysis/trace-processor#python-api). | ||
``` | ||
|
||
Once you have the trace up, you can zoom in/out and pan left/right using the | ||
WASD keys. | ||
|
||
In there, zoom in on the timeframe you want, usually it would be on the last | ||
part (3/4th to the right) and look for a single frame to inspect. | ||
|
||
To be exact, we are looking for: | ||
|
||
* Process: main 0 | ||
* bevy_app | ||
* winit event_handler | ||
* update: | ||
* main_app (for CPU bound problems, for GPU: sub app: name=RenderExtractApp) | ||
* schedule: name=Main | ||
* schedule: name=Update | ||
|
||
Take a look on that are and see what are the main culprits of the time spent. | ||
|
||
NOTE: bevy_framepace::framerate_limiter is intended to take the majority of the | ||
time. This is because its task is to add a sleep/delay so we keep a constant | ||
FPS and we don't burn CPU/GPU resources without need. | ||
|
||
There's additional info on profiling Bevy here: | ||
|
||
https://github.com/bevyengine/bevy/blob/main/docs/profiling.md | ||
|
||
|
||
## WASM Support | ||
|
||
https://bevy-cheatbook.github.io/platforms/wasm.html | ||
|
||
Install deps | ||
|
||
rustup target install wasm32-unknown-unknown | ||
cargo install wasm-server-runner | ||
|
||
Run with: | ||
|
||
cargo run --target wasm32-unknown-unknown --release | ||
|
||
|
||
Current problems: | ||
|
||
* Slow? | ||
* Regular filesystem operation do not work. Bevy Assets do work. Therefore map loading is borked. | ||
* Instant::now does not work. | ||
* Framepace does not work on the browser. Solved. | ||
* Tiled library is never going to work, it opens the files it wants, whenever it wants. It will always fail on WASM. | ||
|
||
Here's a sample on how to load custom assets, so we could load our own data: | ||
https://bevyengine.org/examples/Assets/custom-asset/ | ||
|
||
But most likely this forces us to pre-bake the maps somehow. | ||
|
||
Wait, tiled-rs DOES support wasm. | ||
|
||
Update: It works now, but there's stutter. Mainly because there's only one thread | ||
for executing; and on top of that there's a GC that when it runs it pauses the game. | ||
|
||
--- | ||
Wasm bindgen: | ||
|
||
wasm-pack build --release --target web | ||
|
||
And to test: | ||
|
||
python3 -m http.server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "unhaunter", | ||
"version": "0.1.4", | ||
"files": [ | ||
"unhaunter_bg.wasm", | ||
"unhaunter.js", | ||
"unhaunter.d.ts" | ||
], | ||
"module": "unhaunter.js", | ||
"types": "unhaunter.d.ts", | ||
"sideEffects": [ | ||
"./snippets/*" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* tslint:disable */ | ||
/* eslint-disable */ | ||
/** | ||
*/ | ||
export function wasm_load(): void; | ||
|
||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; | ||
|
||
export interface InitOutput { | ||
readonly memory: WebAssembly.Memory; | ||
readonly wasm_load: () => void; | ||
readonly rust_zstd_wasm_shim_qsort: (a: number, b: number, c: number, d: number) => void; | ||
readonly rust_zstd_wasm_shim_malloc: (a: number) => number; | ||
readonly rust_zstd_wasm_shim_memcmp: (a: number, b: number, c: number) => number; | ||
readonly rust_zstd_wasm_shim_calloc: (a: number, b: number) => number; | ||
readonly rust_zstd_wasm_shim_free: (a: number) => void; | ||
readonly rust_zstd_wasm_shim_memcpy: (a: number, b: number, c: number) => number; | ||
readonly rust_zstd_wasm_shim_memmove: (a: number, b: number, c: number) => number; | ||
readonly rust_zstd_wasm_shim_memset: (a: number, b: number, c: number) => number; | ||
readonly wgpu_compute_pass_set_pipeline: (a: number, b: number) => void; | ||
readonly wgpu_compute_pass_set_bind_group: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_compute_pass_set_push_constant: (a: number, b: number, c: number, d: number) => void; | ||
readonly wgpu_compute_pass_insert_debug_marker: (a: number, b: number, c: number) => void; | ||
readonly wgpu_compute_pass_push_debug_group: (a: number, b: number, c: number) => void; | ||
readonly wgpu_compute_pass_pop_debug_group: (a: number) => void; | ||
readonly wgpu_compute_pass_write_timestamp: (a: number, b: number, c: number) => void; | ||
readonly wgpu_compute_pass_begin_pipeline_statistics_query: (a: number, b: number, c: number) => void; | ||
readonly wgpu_compute_pass_end_pipeline_statistics_query: (a: number) => void; | ||
readonly wgpu_compute_pass_dispatch_workgroups: (a: number, b: number, c: number, d: number) => void; | ||
readonly wgpu_compute_pass_dispatch_workgroups_indirect: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_bundle_set_pipeline: (a: number, b: number) => void; | ||
readonly wgpu_render_bundle_set_bind_group: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_set_vertex_buffer: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_set_push_constants: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_draw: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_draw_indexed: (a: number, b: number, c: number, d: number, e: number, f: number) => void; | ||
readonly wgpu_render_bundle_draw_indirect: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_bundle_draw_indexed_indirect: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_set_pipeline: (a: number, b: number) => void; | ||
readonly wgpu_render_pass_set_bind_group: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_pass_set_vertex_buffer: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_pass_set_push_constants: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_pass_draw: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_pass_draw_indexed: (a: number, b: number, c: number, d: number, e: number, f: number) => void; | ||
readonly wgpu_render_pass_draw_indirect: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_draw_indexed_indirect: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_multi_draw_indirect: (a: number, b: number, c: number, d: number) => void; | ||
readonly wgpu_render_pass_multi_draw_indexed_indirect: (a: number, b: number, c: number, d: number) => void; | ||
readonly wgpu_render_pass_multi_draw_indirect_count: (a: number, b: number, c: number, d: number, e: number, f: number) => void; | ||
readonly wgpu_render_pass_multi_draw_indexed_indirect_count: (a: number, b: number, c: number, d: number, e: number, f: number) => void; | ||
readonly wgpu_render_pass_set_blend_constant: (a: number, b: number) => void; | ||
readonly wgpu_render_pass_set_scissor_rect: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_pass_set_viewport: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; | ||
readonly wgpu_render_pass_set_stencil_reference: (a: number, b: number) => void; | ||
readonly wgpu_render_pass_insert_debug_marker: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_push_debug_group: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_pop_debug_group: (a: number) => void; | ||
readonly wgpu_render_pass_write_timestamp: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_begin_occlusion_query: (a: number, b: number) => void; | ||
readonly wgpu_render_pass_end_occlusion_query: (a: number) => void; | ||
readonly wgpu_render_pass_begin_pipeline_statistics_query: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_end_pipeline_statistics_query: (a: number) => void; | ||
readonly wgpu_render_pass_execute_bundles: (a: number, b: number, c: number) => void; | ||
readonly wgpu_render_pass_set_index_buffer: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_set_index_buffer: (a: number, b: number, c: number, d: number, e: number) => void; | ||
readonly wgpu_render_bundle_pop_debug_group: (a: number) => void; | ||
readonly wgpu_render_bundle_insert_debug_marker: (a: number, b: number) => void; | ||
readonly wgpu_render_bundle_push_debug_group: (a: number, b: number) => void; | ||
readonly __wbindgen_malloc: (a: number, b: number) => number; | ||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; | ||
readonly __wbindgen_export_2: WebAssembly.Table; | ||
readonly _dyn_core__ops__function__FnMut__A_B___Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0fcf9f234871a1b8: (a: number, b: number, c: number, d: number) => void; | ||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h9eb7288e751b06d5: (a: number, b: number, c: number) => void; | ||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h3782a0cfdbf9fc8d: (a: number, b: number, c: number) => void; | ||
readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6adf1446948aa92d: (a: number, b: number) => void; | ||
readonly wasm_bindgen__convert__closures__invoke0_mut__h063b0dcfed0d01d4: (a: number, b: number) => void; | ||
readonly wasm_bindgen__convert__closures__invoke0_mut__h0b513f4138f386c3: (a: number, b: number) => void; | ||
readonly wasm_bindgen__convert__closures__invoke1_mut__ha2b0b048f24e2cd7: (a: number, b: number, c: number) => void; | ||
readonly __wbindgen_free: (a: number, b: number, c: number) => void; | ||
readonly __wbindgen_exn_store: (a: number) => void; | ||
readonly __wbindgen_start: () => void; | ||
} | ||
|
||
export type SyncInitInput = BufferSource | WebAssembly.Module; | ||
/** | ||
* Instantiates the given `module`, which can either be bytes or | ||
* a precompiled `WebAssembly.Module`. | ||
* | ||
* @param {SyncInitInput} module | ||
* | ||
* @returns {InitOutput} | ||
*/ | ||
export function initSync(module: SyncInitInput): InitOutput; | ||
|
||
/** | ||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and | ||
* for everything else, calls `WebAssembly.instantiate` directly. | ||
* | ||
* @param {InitInput | Promise<InitInput>} module_or_path | ||
* | ||
* @returns {Promise<InitOutput>} | ||
*/ | ||
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>; |
Oops, something went wrong.