Compare commits
10 commits
42843165fc
...
0c6e3a9e54
Author | SHA1 | Date | |
---|---|---|---|
0c6e3a9e54 | |||
d8b9a656dc | |||
1761642079 | |||
f1f58a0400 | |||
8d5c58ea7a | |||
4e95e2efca | |||
b221f65748 | |||
d6b8e589fb | |||
e19c099eb8 | |||
2fcf30c819 |
8 changed files with 236 additions and 88 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -58,7 +58,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gilrs",
|
"gilrs",
|
||||||
"minifb",
|
"minifb",
|
||||||
|
@ -609,7 +609,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pirates"
|
name = "pirates"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libm",
|
"libm",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
|
|
|
@ -4,8 +4,12 @@ members = [
|
||||||
"client",
|
"client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[profile.minsized]
|
[profile.minsized]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
opt-level = 'z'
|
opt-level = 's'
|
||||||
strip = true
|
strip = true
|
||||||
|
|
23
README.md
23
README.md
|
@ -2,15 +2,32 @@
|
||||||
|
|
||||||
my [entry](https://js13kgames.com/entries/simple-sailing-simulator) to js13kgames 2023
|
my [entry](https://js13kgames.com/entries/simple-sailing-simulator) to js13kgames 2023
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
> Brave the north wind and search for York, or simply explore.
|
> Brave the north wind and search for York, or simply explore.
|
||||||
|
|
||||||
### Controls
|
### Controls
|
||||||
+/-: zoom
|
|keyboard | controller | action
|
||||||
A: rudder left
|
|---|---|---
|
||||||
D: rudder right
|
|`A`|`D-Left`|rudder left
|
||||||
|
|`D`|`D-Right`|rudder right
|
||||||
|
||Left Stick X|rudder
|
||||||
|
|`+`|`D-Up`|zoom in
|
||||||
|
|`-`|`D-Down`|zoom out
|
||||||
|
||Right Stick Y|zoom
|
||||||
|
|`E`|`B`|raise sails
|
||||||
|
|`Q`|`A`|lower sails
|
||||||
|
||Left Stick Y + Left Bumper|control sails
|
||||||
|
|arrow keys|Right Stick + Right Bumper|pan camera
|
||||||
|
|`R`|`Y`|reset sailboat
|
||||||
|
|`/`|`X`|reset camera
|
||||||
|
|Esc||quit
|
||||||
|
|
||||||
Your sailboat travels fastest going perpendicular to the wind.
|
Your sailboat travels fastest going perpendicular to the wind.
|
||||||
|
|
||||||
|
#### Installation
|
||||||
|
|
||||||
|
download the [latest release](https://github.com/Speedy6451/simplesailing/releases/latest)
|
||||||
|
|
||||||
#### Building from source
|
#### Building from source
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A game about wind"
|
description = "A game about wind"
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use std::time::SystemTime;
|
||||||
use minifb::{Key, ScaleMode, Window, WindowOptions, Scale};
|
use minifb::{Key, ScaleMode, Window, WindowOptions, Scale};
|
||||||
extern crate pirates;
|
extern crate pirates;
|
||||||
use pirates::{WIDTH, HEIGHT};
|
use pirates::{WIDTH, HEIGHT, Input};
|
||||||
#[cfg(feature = "gilrs")]
|
#[cfg(feature = "gilrs")]
|
||||||
use gilrs::{Axis, Gilrs, Button};
|
use gilrs::{Axis, Gilrs, Button};
|
||||||
|
use pirates::Input::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(feature = "gilrs")]
|
#[cfg(feature = "gilrs")]
|
||||||
|
@ -26,17 +28,17 @@ fn main() {
|
||||||
|
|
||||||
let mut buffer: Vec<u32> = Vec::with_capacity(WIDTH * HEIGHT);
|
let mut buffer: Vec<u32> = Vec::with_capacity(WIDTH * HEIGHT);
|
||||||
|
|
||||||
fn keyboard_input(key: u8) {
|
fn keyboard_input(key: Input) {
|
||||||
unsafe {
|
unsafe {
|
||||||
pirates::KEYCODE[0] = key;
|
pirates::KEYCODE[0] = key as u8;
|
||||||
pirates::keyboard_input();
|
pirates::keyboard_input();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analog_input(chan: u8, val: f32) {
|
fn analog_input(chan: Input, val: f32) {
|
||||||
let val = (val * 127.0) as i8;
|
let val = (val * 127.0) as i8;
|
||||||
unsafe {
|
unsafe {
|
||||||
pirates::KEYCODE[0] = chan;
|
pirates::KEYCODE[0] = chan as u8;
|
||||||
pirates::KEYCODE[1] = (val + 127) as u8;
|
pirates::KEYCODE[1] = (val + 127) as u8;
|
||||||
pirates::keyboard_input();
|
pirates::keyboard_input();
|
||||||
}
|
}
|
||||||
|
@ -45,9 +47,16 @@ fn main() {
|
||||||
#[cfg(feature = "gamepad")]
|
#[cfg(feature = "gamepad")]
|
||||||
let mut gamepad_handle = None;
|
let mut gamepad_handle = None;
|
||||||
|
|
||||||
|
let mut frame_start: SystemTime = SystemTime::now();
|
||||||
|
|
||||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
|
let last_frame = frame_start.elapsed().unwrap().as_micros();
|
||||||
|
frame_start = SystemTime::now();
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
pirates::LAST_FRAME_TIME = last_frame as f32 / 1000.0;
|
||||||
|
|
||||||
pirates::frame_entry();
|
pirates::frame_entry();
|
||||||
for pix in pirates::BUFFER {
|
for pix in pirates::BUFFER {
|
||||||
// AABBGGRR to 00RRGGBB
|
// AABBGGRR to 00RRGGBB
|
||||||
|
@ -66,43 +75,64 @@ fn main() {
|
||||||
#[cfg(feature = "gamepad")]
|
#[cfg(feature = "gamepad")]
|
||||||
if let Some(gamepad) = gamepad_handle.map(|h| gilrs.gamepad(h)) {
|
if let Some(gamepad) = gamepad_handle.map(|h| gilrs.gamepad(h)) {
|
||||||
gamepad.axis_data(Axis::LeftStickX).map(|axis| {
|
gamepad.axis_data(Axis::LeftStickX).map(|axis| {
|
||||||
analog_input(1, axis.value());
|
analog_input(AxisRudder, axis.value());
|
||||||
});
|
});
|
||||||
|
if gamepad.is_pressed(Button::LeftTrigger) {
|
||||||
|
gamepad.axis_data(Axis::LeftStickY).map(|axis| {
|
||||||
|
analog_input(AxisSail, axis.value());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if gamepad.is_pressed(Button::South) {
|
||||||
|
keyboard_input(RaiseSail)
|
||||||
|
}
|
||||||
|
if gamepad.is_pressed(Button::East) {
|
||||||
|
keyboard_input(LowerSail)
|
||||||
|
}
|
||||||
if gamepad.is_pressed(Button::RightTrigger) {
|
if gamepad.is_pressed(Button::RightTrigger) {
|
||||||
gamepad.axis_data(Axis::RightStickY).map(|axis| {
|
gamepad.axis_data(Axis::RightStickY).map(|axis| {
|
||||||
analog_input(3, dbg!(axis.value()));
|
analog_input(AxisPanY, axis.value());
|
||||||
});
|
});
|
||||||
gamepad.axis_data(Axis::RightStickX).map(|axis| {
|
gamepad.axis_data(Axis::RightStickX).map(|axis| {
|
||||||
analog_input(4, dbg!(axis.value()));
|
analog_input(AxisPanX, axis.value());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
gamepad.axis_data(Axis::RightStickY).map(|axis| {
|
gamepad.axis_data(Axis::RightStickY).map(|axis| {
|
||||||
analog_input(0, dbg!(axis.value()));
|
analog_input(AxisZoom, axis.value());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if gamepad.is_pressed(Button::West) {
|
||||||
|
keyboard_input(ResetCamera)
|
||||||
|
}
|
||||||
|
if gamepad.is_pressed(Button::North) {
|
||||||
|
keyboard_input(ResetBoat)
|
||||||
|
}
|
||||||
if gamepad.is_pressed(Button::DPadLeft) {
|
if gamepad.is_pressed(Button::DPadLeft) {
|
||||||
keyboard_input(65)
|
keyboard_input(PanLeft)
|
||||||
}
|
}
|
||||||
if gamepad.is_pressed(Button::DPadRight) {
|
if gamepad.is_pressed(Button::DPadRight) {
|
||||||
keyboard_input(68)
|
keyboard_input(PanRight)
|
||||||
}
|
}
|
||||||
if gamepad.is_pressed(Button::DPadUp) {
|
if gamepad.is_pressed(Button::DPadUp) {
|
||||||
keyboard_input(61)
|
keyboard_input(ZoomIn)
|
||||||
}
|
}
|
||||||
if gamepad.is_pressed(Button::DPadDown) {
|
if gamepad.is_pressed(Button::DPadDown) {
|
||||||
keyboard_input(173)
|
keyboard_input(ZoomOut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.get_keys().iter().for_each(|key| match key {
|
window.get_keys().iter().for_each(|key| match key {
|
||||||
Key::A => keyboard_input(65),
|
Key::A => keyboard_input(RudderLeft),
|
||||||
Key::D => keyboard_input(68),
|
Key::D => keyboard_input(RudderRight),
|
||||||
Key::Equal => keyboard_input(61),
|
Key::Equal => keyboard_input(ZoomIn),
|
||||||
Key::Minus => keyboard_input(173),
|
Key::Minus => keyboard_input(ZoomOut),
|
||||||
Key::Up => keyboard_input(38),
|
Key::Up => keyboard_input(PanUp),
|
||||||
Key::Down => keyboard_input(40),
|
Key::Down => keyboard_input(PanDown),
|
||||||
Key::Left => keyboard_input(37),
|
Key::Left => keyboard_input(PanLeft),
|
||||||
Key::Right => keyboard_input(39),
|
Key::Right => keyboard_input(PanRight),
|
||||||
|
Key::R => keyboard_input(ResetBoat),
|
||||||
|
Key::Slash => keyboard_input(ResetCamera),
|
||||||
|
Key::E => keyboard_input(RaiseSail),
|
||||||
|
Key::Q => keyboard_input(LowerSail),
|
||||||
_ => (),
|
_ => (),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,20 @@ var memory;
|
||||||
var exports;
|
var exports;
|
||||||
const width = 160;
|
const width = 160;
|
||||||
const height = 144;
|
const height = 144;
|
||||||
|
const TIME_GAIN = 0.1; // game is unplayable at full speed with wasm refresh rates
|
||||||
|
|
||||||
function blit_frame() {
|
function blit_frame() {
|
||||||
|
// this is required when using an allocator from wasm as there is
|
||||||
|
// no way to update the internal pointer when linear memory shifts
|
||||||
|
image = new ImageData(
|
||||||
|
new Uint8ClampedArray(
|
||||||
|
memory.buffer,
|
||||||
|
exports.BUFFER.value,
|
||||||
|
4 * width * height,
|
||||||
|
),
|
||||||
|
width,
|
||||||
|
);
|
||||||
|
|
||||||
ctx.putImageData(image, 0, 0);
|
ctx.putImageData(image, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,21 +59,27 @@ async function init() {
|
||||||
document.getElementById("body").onkeydown=keyboard_callback;
|
document.getElementById("body").onkeydown=keyboard_callback;
|
||||||
|
|
||||||
memory = instance.exports.memory
|
memory = instance.exports.memory
|
||||||
const buffer_address = instance.exports.BUFFER.value;
|
|
||||||
image = new ImageData(
|
|
||||||
new Uint8ClampedArray(
|
|
||||||
memory.buffer,
|
|
||||||
buffer_address,
|
|
||||||
4 * width * height,
|
|
||||||
),
|
|
||||||
width,
|
|
||||||
);
|
|
||||||
|
|
||||||
instance.exports.frame_entry();
|
instance.exports.frame_entry();
|
||||||
ctx.textBaseline = 'top'
|
ctx.textBaseline = 'top'
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
|
|
||||||
const render = () => {
|
var last;
|
||||||
|
var elapsed;
|
||||||
|
|
||||||
|
const render = (time) => {
|
||||||
|
if(!last) { elapsed = 0; }
|
||||||
|
else {
|
||||||
|
elapsed = time-last;
|
||||||
|
}
|
||||||
|
last=time;
|
||||||
|
|
||||||
|
if(!elapsed) {
|
||||||
|
elapsed = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FRAME_TIME = new Float32Array(exports.memory.buffer, exports.LAST_FRAME_TIME, 1);
|
||||||
|
FRAME_TIME[0] = elapsed * TIME_GAIN;
|
||||||
|
|
||||||
instance.exports.frame_entry();
|
instance.exports.frame_entry();
|
||||||
|
|
||||||
requestAnimationFrame(render);
|
requestAnimationFrame(render);
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pirates"
|
name = "pirates"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["wasm"]
|
default = ["wasm"]
|
||||||
wasm = ["wee_alloc"]
|
wasm = ["wee_alloc"]
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
opt-level = 's'
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib", "cdylib"]
|
crate-type = ["lib", "cdylib"]
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ use spin::Mutex;
|
||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::noise::lerp;
|
|
||||||
|
|
||||||
mod sampler;
|
mod sampler;
|
||||||
mod noise;
|
mod noise;
|
||||||
|
|
||||||
|
@ -47,6 +45,9 @@ fn draw_text(text: &str, x: i32, y: i32, size: u8) {
|
||||||
pub const WIDTH: usize = 160;
|
pub const WIDTH: usize = 160;
|
||||||
pub const HEIGHT: usize = 144;
|
pub const HEIGHT: usize = 144;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut LAST_FRAME_TIME: f32 = 0.0;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mut BUFFER: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
pub static mut BUFFER: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];
|
||||||
|
|
||||||
|
@ -77,7 +78,12 @@ static MAP: [u8; MAP_WIDTH * MAP_HEIGHT] = [ // should deflate to smaller than b
|
||||||
|
|
||||||
static CAMERA: Mutex<[f32; 3]> = Mutex::new([0.0, 0.0, 0.18]);
|
static CAMERA: Mutex<[f32; 3]> = Mutex::new([0.0, 0.0, 0.18]);
|
||||||
|
|
||||||
static BOAT: Mutex<Boat> = Mutex::new(Boat { x: 0.0, y: 0.0, theta: 0.0, vel: 0.0 });
|
static BOAT: Mutex<Boat> = Mutex::new(Boat {
|
||||||
|
x: 0.0, y: 0.0,
|
||||||
|
theta: 0.0,
|
||||||
|
vel: 0.0,
|
||||||
|
sail: 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn keyboard_input() {
|
pub unsafe extern fn keyboard_input() {
|
||||||
|
@ -105,50 +111,71 @@ fn render_frame(buffer: &mut [u32; WIDTH*HEIGHT]) {
|
||||||
let mut camera = CAMERA.lock();
|
let mut camera = CAMERA.lock();
|
||||||
let mut boat = BOAT.lock();
|
let mut boat = BOAT.lock();
|
||||||
|
|
||||||
//camera[0] += 1.0;
|
let mut gain = unsafe { // very much an approximation of constant-velocity animation
|
||||||
|
LAST_FRAME_TIME / (1000.0 / 20.0) // normalize to 20fps, cap simulation at 10fps
|
||||||
|
};
|
||||||
while let Some(key) = INPUTS.lock().pop() {
|
while let Some(key) = INPUTS.lock().pop() {
|
||||||
match key[0] {
|
use Input::*;
|
||||||
38 => camera[1] -= 10.0*camera[2], // up
|
let input = (key[0] as u8).try_into().ok();
|
||||||
40 => camera[1] += 10.0*camera[2], // down
|
if input.is_none() { continue; }
|
||||||
37 => camera[0] -= 10.0*camera[2], // left
|
match input.unwrap() {
|
||||||
39 => camera[0] += 10.0*camera[2], // right
|
PanUp => camera[1] -= gain*10.0*camera[2], // up
|
||||||
61 => camera[2] *= 0.9, // +
|
PanDown => camera[1] += gain*10.0*camera[2], // down
|
||||||
173 => camera[2] *= 1.1, // -
|
PanLeft => camera[0] -= gain*10.0*camera[2], // left
|
||||||
65 => boat.theta -= 10.0, // A
|
PanRight => camera[0] += gain*10.0*camera[2], // right
|
||||||
68 => boat.theta += 10.0, // D
|
ResetCamera => {
|
||||||
0 => camera[2] *= 1.0 - (key[1] as f32 - 127.0) * 0.0004, // analog zoom
|
*camera = [
|
||||||
1 => boat.theta += (key[1] as f32 - 127.0) * 0.031, // analog rudder
|
noise::lerp(camera[0], 0.00, (gain * 0.25).min(1.0)),
|
||||||
3 => camera[1] -= (key[1] as f32 - 127.0) * 0.004, // pan[y]
|
noise::lerp(camera[1], 0.00, (gain * 0.25).min(1.0)),
|
||||||
4 => camera[0] += (key[1] as f32 - 127.0) * 0.004, // pan[x]
|
noise::lerp(camera[2], 0.18, (gain * 0.25).min(1.0)),
|
||||||
_ => {}
|
]
|
||||||
|
}, // reset camera (/)
|
||||||
|
ResetBoat => boat.set_pos(Vector2::zeros()), // reset boat (r)
|
||||||
|
ZoomIn => camera[2] *= 1.0 - 0.1*gain, // +
|
||||||
|
ZoomOut => camera[2] *= 1.0 + 0.1*gain, // -
|
||||||
|
RudderLeft => boat.theta -= gain*10.0, // A
|
||||||
|
RudderRight => boat.theta += gain*10.0, // D
|
||||||
|
AxisZoom => camera[2] *= 1.0 - (key[1] as f32 - 127.0) * 0.0004 * gain, // analog zoom
|
||||||
|
AxisRudder => boat.theta += gain * (key[1] as f32 - 127.0) * 0.062, // analog rudder
|
||||||
|
AxisPanY => camera[1] -= gain * (key[1] as f32 - 127.0) * 0.1 * camera[2], // pan[y]
|
||||||
|
AxisPanX => camera[0] += gain * (key[1] as f32 - 127.0) * 0.1 * camera[2], // pan[x]
|
||||||
|
AxisSail => boat.sail += gain * (key[1] as f32 - 127.0) * 0.0013, // sail
|
||||||
|
RaiseSail => boat.sail += gain * 0.062, // E
|
||||||
|
LowerSail => boat.sail -= gain * 0.062, // Q
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boat.sail = boat.sail.clamp(0.0, 1.5);
|
||||||
|
|
||||||
let wind = 0.0/RAD_TO_DEG;
|
let wind = 0.0/RAD_TO_DEG;
|
||||||
let vel = boat.get_velocity(wind);
|
|
||||||
|
|
||||||
let camera_vec = Vector2::new(camera[0],camera[1]);
|
let step = 1.0; // 50ms
|
||||||
let boat_pos = boat.get_pos();
|
while gain > 0.0 { // when draw fps is low, physics will still run at a minimum of 20fps
|
||||||
|
gain -= step;
|
||||||
|
let gain = gain + step;
|
||||||
|
|
||||||
|
let vel = boat.get_velocity(wind);
|
||||||
|
|
||||||
let depth = -sample_world(boat_pos+HALF, rand);
|
let depth = -sample_world(boat.get_pos()+HALF, rand);
|
||||||
if depth < -0.04 {
|
if depth < -0.04 {
|
||||||
boat.vel = 0.0;
|
boat.vel = 0.0;
|
||||||
} else if depth < 0.0 {
|
} else if depth < 0.0 {
|
||||||
boat.vel *= (1.0 - depth) * 0.25;
|
boat.vel *= 1.0 + (depth * gain * 30.2).min(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if depth > -0.04 {
|
if depth > -0.04 {
|
||||||
boat.go_smooth(-vel * 0.42);
|
boat.vel = noise::lerp(boat.vel, vel * 0.82, 0.13 * gain);
|
||||||
|
boat.go(gain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw sea
|
// draw sea
|
||||||
const HALF: Vector2<f32> = Vector2::new(WIDTH as f32 / 2.0, HEIGHT as f32 / 2.0);
|
const HALF: Vector2<f32> = Vector2::new(WIDTH as f32 / 2.0, HEIGHT as f32 / 2.0);
|
||||||
|
let camera_vec = Vector2::new(camera[0],camera[1]);
|
||||||
|
|
||||||
#[cfg(feature = "rayon")]
|
#[cfg(feature = "rayon")]
|
||||||
let mut buffer_iter = buffer.par_iter_mut();
|
let buffer_iter = buffer.par_iter_mut();
|
||||||
#[cfg(not(feature = "rayon"))]
|
#[cfg(not(feature = "rayon"))]
|
||||||
let mut buffer_iter = buffer.iter_mut();
|
let buffer_iter = buffer.iter_mut();
|
||||||
|
|
||||||
buffer_iter.enumerate().for_each(|pix| {
|
buffer_iter.enumerate().for_each(|pix| {
|
||||||
let y = pix.0 / WIDTH;
|
let y = pix.0 / WIDTH;
|
||||||
|
@ -157,7 +184,7 @@ fn render_frame(buffer: &mut [u32; WIDTH*HEIGHT]) {
|
||||||
point -= HALF;
|
point -= HALF;
|
||||||
point *= camera[2];
|
point *= camera[2];
|
||||||
point += HALF;
|
point += HALF;
|
||||||
let n = sample_world(point+camera_vec+boat_pos, rand);
|
let n = sample_world(point+camera_vec+boat.get_pos(), rand);
|
||||||
*pix.1 =
|
*pix.1 =
|
||||||
if n > 0.1 {
|
if n > 0.1 {
|
||||||
let n = (n+0.1) * 300.0;
|
let n = (n+0.1) * 300.0;
|
||||||
|
@ -237,8 +264,7 @@ fn draw_tri(color: u32, buffer: &mut [u32; WIDTH*HEIGHT], p1: Vector2<f32>, p2:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_world(point: Vector2<f32>, rand: PerlinBuf) -> f32 {
|
fn sample_world(point: Vector2<f32>, rand: PerlinBuf) -> f32 {
|
||||||
let offset = Vector2::new(64480.0, 7870.0);
|
let offset = Vector2::new(64492.0, 7892.0);
|
||||||
//240.0,240.0
|
|
||||||
let point = point + offset;
|
let point = point + offset;
|
||||||
let mut n = 0.0;
|
let mut n = 0.0;
|
||||||
n += (sampler::sample_map_inter(point / 64.0, &MAP)-0.5)* 0.6;
|
n += (sampler::sample_map_inter(point / 64.0, &MAP)-0.5)* 0.6;
|
||||||
|
@ -257,6 +283,8 @@ struct Boat {
|
||||||
y: f32,
|
y: f32,
|
||||||
theta: f32,
|
theta: f32,
|
||||||
vel: f32,
|
vel: f32,
|
||||||
|
/// sail height 0-1
|
||||||
|
sail: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Boat {
|
impl Boat {
|
||||||
|
@ -270,26 +298,81 @@ impl Boat {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_intensity(self: &Self, wind_direction: f32) -> f32 {
|
fn get_intensity(self: &Self, wind_direction: f32) -> f32 {
|
||||||
libm::sinf((self.theta-wind_direction)/RAD_TO_DEG)
|
libm::sinf((self.theta-wind_direction)/RAD_TO_DEG) * self.sail
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_velocity(self: &Self, wind_direction: f32) -> f32 {
|
fn get_velocity(self: &Self, wind_direction: f32) -> f32 {
|
||||||
libm::fabsf(self.get_intensity(wind_direction))
|
libm::fabsf(self.get_intensity(wind_direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn go_smooth(self: &mut Self, vel: f32) {
|
fn go(self: &mut Self, gain: f32) {
|
||||||
self.vel = noise::lerp(self.vel, vel, 0.09);
|
|
||||||
|
|
||||||
self.go(self.vel);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn go(self: &mut Self, velocity: f32) {
|
|
||||||
let cos = libm::cosf((self.theta+ 45.0)/RAD_TO_DEG);
|
let cos = libm::cosf((self.theta+ 45.0)/RAD_TO_DEG);
|
||||||
let sin = libm::sinf((self.theta+ 45.0)/RAD_TO_DEG);
|
let sin = libm::sinf((self.theta+ 45.0)/RAD_TO_DEG);
|
||||||
let unit = Vector2::new(
|
let unit = Vector2::new(
|
||||||
cos - sin,
|
cos - sin,
|
||||||
sin + cos);
|
sin + cos);
|
||||||
self.set_pos(self.get_pos() + unit * velocity);
|
self.set_pos(self.get_pos() + unit * -self.vel * gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for Input {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(input: u8) -> Result<Self, Self::Error> {
|
||||||
|
use Input::*;
|
||||||
|
match input {
|
||||||
|
38 => Ok(PanUp),
|
||||||
|
40 => Ok(PanDown),
|
||||||
|
37 => Ok(PanLeft),
|
||||||
|
39 => Ok(PanRight),
|
||||||
|
61 => Ok(ZoomIn),
|
||||||
|
173 => Ok(ZoomOut),
|
||||||
|
65 => Ok(RudderLeft),
|
||||||
|
68 => Ok(RudderRight),
|
||||||
|
69 => Ok(RaiseSail),
|
||||||
|
81 => Ok(LowerSail),
|
||||||
|
82 => Ok(ResetBoat),
|
||||||
|
191 => Ok(ResetCamera),
|
||||||
|
0 => Ok(AxisZoom),
|
||||||
|
1 => Ok(AxisRudder),
|
||||||
|
3 => Ok(AxisPanY),
|
||||||
|
4 => Ok(AxisPanX),
|
||||||
|
5 => Ok(AxisSail),
|
||||||
|
_ => Err("unmapped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Input {
|
||||||
|
/// Up Arrow
|
||||||
|
PanUp = 38,
|
||||||
|
/// Down Arrow
|
||||||
|
PanDown = 40,
|
||||||
|
/// Left Arrow
|
||||||
|
PanLeft = 37,
|
||||||
|
/// Right Arrow
|
||||||
|
PanRight = 39,
|
||||||
|
/// +
|
||||||
|
ZoomIn = 61,
|
||||||
|
/// -
|
||||||
|
ZoomOut = 173,
|
||||||
|
/// A
|
||||||
|
RudderLeft = 65,
|
||||||
|
/// D
|
||||||
|
RudderRight = 68,
|
||||||
|
/// "/"
|
||||||
|
ResetCamera = 191,
|
||||||
|
/// R
|
||||||
|
ResetBoat = 82,
|
||||||
|
/// E
|
||||||
|
RaiseSail = 69,
|
||||||
|
/// Q
|
||||||
|
LowerSail = 81,
|
||||||
|
AxisZoom = 0,
|
||||||
|
AxisRudder = 1,
|
||||||
|
AxisPanY = 3,
|
||||||
|
AxisPanX = 4,
|
||||||
|
AxisSail = 5,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue