A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇.

Overview


Markdownify
Hebi 🐍

A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, (へび).

Configuration

One of the things that sets apart Hebi from other snake clones is its rich configuration options. You can configure pretty much everything: the map, tick speed, food spawn rate, window scale, etc. You can even make your own custom color themes in addition to those provided! Keep in mind that Hebi is still very much a work-in-progress, so the names, behavior, and functionality of configuration options may change in the future.

To start configuring your game, create a config.toml file next to the Hebi executable.

Example config.toml

theme = "solarized_light"
seed = 42
food_ticks = 8

[map]
type = "corridors"
corridor_width = 2

Available configuration options

Name Type Default value Function
theme String "dracula" Sets what in-game color theme Hebi should use. For example, theme = "cavern" would load the theme file themes/cavern.toml. See Themes for more information on creating custom themes.
seed u64 Random Sets what seed should be used for deciding snake spawn locations, food spawn locations, and food colors. Since this is an unsigned number, it must be positive and less than or equal to 18446744073709551615.
map Map N/A Sets the map data. See Maps for more info.
grid_scale u32 36 Sets the number of pixels per grid square.
tick_length f64 0.2 Sets the amount of time between in-game ticks in seconds. For a more challenging gameplay experience, try setting this to 0.15 or 0.1. For an easier gameplay experience for those with a slow reaction time, try setting this to 0.25 or 0.3.
food_ticks u32 16 Sets the number of ticks between each food being spawned.
snake_spawn_segments u32 2 Sets the number of segments the snake spawns with, including the head.
💥 Modifying this option from its default value may cause the game to crash.
snake_segment_despawn_interval f64 0.1 Sets the interval between each segment despawning upon snake death in seconds. You can set this to 0 if you want the entire snake to despawn at once.
snake_respawn_delay f64 0.5 Sets the delay in seconds between all segments despawning upon snake death and respawning.
eat_audio String "eat.wav" Sets the audio file for the destruction of the snake's head and segments. The source for the default audio file is jsfxr. For example, eat_audio = "munch.wav" (fictional file) would load the audio file assets/sounds/munch.wav. Both .mp3 and .wav files are supported.
destroy_audio String "destroy.wav" Sets the audio file for the destruction of the snake's head and segments. The source for the default audio file is jsfxr.
spawn_food_audio String "spawn_food.wav" Sets the audio file for when food is spawned. The source for the default audio file is jsfxr.
spawn_snake_audio String "spawn_snake.wav" Sets the audio file for when the snake is spawned. The source for the default audio file is jsfxr.

Themes

Along with the provided themes dracula, solarized_dark, solarized_light, and cavern, you can create your own themes! To get started, create a new file my_theme.toml in the themes folder, and open it in a text editor. To start off with, you can copy in the theme settings for cavern:

walls = "222233"
background = "000011"
snake = "ddddee"
food = ["5599ff", "ffc455", "ff6f55"]

To change a color, replace any of the hex codes provided, and make sure to remove the leading #. For food, you can put in as many colors as you like. If you decide to only have one food color, make sure to wrap it on square brackets: food = ["5599ff"].

To actually use your theme, see Configuration and add theme to your configuration file. If the theme can't file can't be found, the game will run, but as the colors are missing everything will be magenta. Likewise, if a color is missing or incorrectly formatted in your theme file, it will also show as magenta in-game.

Maps

Hebi currently ships with three different map generation modes: default, corridors, and custom.

Default

The default map type is the simplest, and it is what runs by default without any changes to the configuration file. To begin modifying, add the following to your config.toml file:

[map]
type = "default"

To override the default generation values, see the following table.

Configuration options

Name Type Default value Function
width u32 17 Sets game grid width, including walls.
height u32 13 Sets game grid height, including walls.
corner_walls u32 2 Sets the width and height of the corner walls. If you don't want any corner walls and an empty map, just set this to 0.
corner_walls_offset i32 2 Sets the offset on the x- and y-axes of each of the corner walls from their respective corners of the map.

Corridors

The corridors map type is the only (relatively) complex generation type in the game currently. It sets up a bunch of vertical (or horizontal) corridors in the map of varying heights to increase game difficulty. To begin modifying add the following to your config.toml file:

[map]
type = "corridors"

To override the default generation values, see the following table.

Configuration options

Name Type Default value Function
width u32 34 Sets game grid width, including walls.
height u32 17 Sets game grid height, including walls.
horizontal bool false Sets whether to generate the map horizontally instead of vertically.
corridor_width u32 3 Sets the width (height if horizontal = true) of each corridor. Corridors at the end of the map may be wider. This shouldn't be set to less than 2, since 1-wide corridors are dead ends and cannot be exited by the snake.
corridor_height u32 10 Sets the maximum height (width if horizontal = true) of each corridor. For more control, see wall_variance.
top_corridor_offset i32 3 Sets the horizontal (vertical if horizontal = true) offset of the top row of corridors.
bottom_corridor_offset i32 0 Sets the horizontal (vertical if horizontal = true) offset of the bottom row of corridors.
wall_variance f32 0.5 Sets the variance in the height (width if horizontal = true) of each wall. For example, with the default values where corridor_height = 10 and wall_variance = 0.5, that means that the actual heights of the walls in-game can vary anywhere in the top 50% of the maximum height, e.g. anywhere from 5 to 10.

Custom

If you aren't satisfied with the provided map generators, you can make your own maps! To get started, add the following to your config.toml file:

[map]
type = "custom"
data = """
#################
#               #
#  >      >  <  #
#  #      #  #  #
#  #      #  #  #
#               #
#  > <   >      #
#  # #   #      #
#  # #   # ##v  #
#               #
#################
"""

In the data field is where you make your map. A space means empty space and a pound sign # means a wall. Additionally, you must place at least one snake spawn point. A power symbol (^) is an up-facing spawn, a lower-case V (v) is down-facing spawn, a less-than sign (<) is a left-facing spawn, and a greater-than sign (>) is a right-facing spawn.

Contributing

If you feel like there's something you'd like to add to the game, feel free to make a fork and submit a pull request! I'll try to review it as soon as possible. If there's an issue with how I've structured the code in the project and you feel like there's a better way, however, please make an issue instead. Hebi is a learning project for me to learn Rust and Bevy, and I'd like to implement most of the core changes myself.

If you want to create a custom map generation type, a good starting point would be looking at src/maps/default.rs. Once you've got your generator working, submit it with a pull request! The more variety in game maps the better.

Building from source

Do you like what you see? Or maybe, you'd like to contribute to the project?

If you answered "Yes", then follow through these steps to get the game up and running, built right from source.

If you don't have Rust installed already, see the installation guide on Rust's official website and then come back once you're done.

Before we get to work, please note that Hebi uses some nightly Rust features (strip, which is one of the features used to create smaller binary sizes), that haven't been introduced to the stable channel. If you want to build Hebi without nightly Rust, remove or comment out the lines cargo-features = ["strip"] from the top and strip = true under [profile.release] in Cargo.toml.

  1. Don't mind installing the channel? Then, let's default to it. If it's not already installed, then rustup will take care of that for you:
rustup default nightly
  1. Now, clone the repository:
git clone https://github.com/ElnuDev/hebi.git
  1. Great, let's jump inside the directory:
cd hebi
  1. Okay, let's proceed by building the game; you know, turning that code into an executable.
  • If you plan on contributing towards Hebi, we recommend building without the --release flag to cut down on compilation time:
cargo build
  • If you're ready to take your Snake game to the next level and want the best experience, then --release is your friend:
cargo build --release
  1. Now that you've built it yourself, you might want to configure Hebi.

Compressing with upx

The Linux and Windows (gross) builds provided in the releases section are compressed with UPX, the Ultimate Packer for eXecutables. UPX can reduce the binary size of Hebi by around 75%. If you want to make a distributable build of Hebi and have already used the --release flag for cargo build, you may also want to consider UPX. The cons of using UPX is that Hebi may be more likely to be flagged as malware by antivirus software, and there could be a performance penalty (unconfirmed, though it doesn't really matter much for such a simple game).

  1. Install UPX

If you're on a Debian-based Linux distribution, you can install it with apt:

sudo apt install upx

If you're on Windows, you can download it from the UPX GitHub repository's releases section. To my knowledge, it's not available for winget, Window's new package manager, just yet.

  1. Compress the binary

Ensure you're in the cloned Hebi folder first.

Linux:

upx --best --lzma target/release/hebi

Windows:

path\to\upx.exe --best --lzma target\release\hebi.exe

Distribution

For distribution, make sure to include the executable (hebi on Linux, hebi.exe on Windows) next to the themes and assets folder.

Comments
  • Implemented control remapping

    Implemented control remapping

    Closes #16.

    Changes

    To do this, I added a new section to the Config, which can take an array of input bindings for each command in the game.

    Since the convenient way to write this down in configuration is not strikingly efficient for runtime querying, I created a new resource type DirectionalControls that simplifies runtime access.

    Refactored the snake_movement_input system to use this new resource, and cleaned up a bit of its code too, by breaking up the nesting in iteration. While it technically runs faster when the player presses multiple keys in one frame, it was unlikely to be a problem, so this was mainly a readability change. This way it is also a bit easier to add different event-reading loops later.

    Usage

    Players can change their control configuration like this (likewise for down, left, right):

    [controls]
    up = [ { device = "keyboard", code = 72 } ]
    

    Sadly, Bevy does not provide conversions from named keys to string and vice-versa, so I had to use their scan codes instead. Good part is that it's keyboard-layout dependent, bad part is that it is rather obscure to set up (I found them by putting a print on key presses and taking note of the codes for each key).

    Extensibility

    When time comes for #17, the following changes will be needed:

    1. Add a new variant to the config::Binding enum with appropriate data;
    2. Consume those variants when constructing DirectionalControls (the compiler will yell the location at you);
    3. Add a new method to the impl DirectionalControls to read the gamepad event;
    4. Call that method in the input system within the processing loop for gamepads.
    enhancement 
    opened by kroltan 13
  • Custom level layouts in config

    Custom level layouts in config

    I imagine this is something you might want to do yourself, but if you need some inspiration you can use this PR as a reference! Do not feel like you need to merge this. 😅


    Basically, I removed the grid_width, grid_height and corner_walls fields from Config, and instead created an enum called Map, which can be one of two variants:

    • Box layout, which has the aforementioned properties and uses the existing level generation code;
    • Custom layout, which allows whoever is writing config.toml to draw a map with ASCII.

    To be able to do that, I had to refactor the coordinate conversion function to use a new unified resource for the grid dimensions, since it can be obtained from possibly many locations now.

    Here's how config.toml feels like now, using a custom map (before you ask, yes, it is. 😄):

    theme = "solarized_light"
    tick_length = 0.15
    food_ticks = 8
    
    [map]
    type = "custom"
    data = """
    #################
    #               #
    #  >      >  <  #
    #  #      #  #  #
    #  #      #  #  #
    #               #
    #  > <   >      #
    #  # #   #      #
    #  # #   # ##v  #
    #               #
    #################
    """
    

    Or using the generation algorithm:

    theme = "solarized_light"
    tick_length = 0.15
    food_ticks = 8
    
    [map]
    type = "box"
    width = 17
    height = 13
    corner_walls = true
    
    enhancement 
    opened by kroltan 5
  • Use .wav audio directly instead of converting to .mp3

    Use .wav audio directly instead of converting to .mp3

    Currently, the .wav files in the dev_assets folder cause errors when used, even with the wav feature of Bevy enabled. See this thread on the Bevy Discord server for more info. The files seem to play fine, so I'm not sure if this is some issue with the encoding of the files that jsfxr outputs, or if there's a potential bug in Bevy. Either way, it'd be way cleaner use the original .wav files directly instead of having to convert them .mp3 using a shell script like is being done currently and store both in this repository.

    bug enhancement help wanted 
    opened by ElnuDev 3
  • don't segfault when process terminates

    don't segfault when process terminates

    Hi :)

    I'm not sure if you were able to reproduce this on your end, but when I kill the process the compiler throws:

    thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:329:61
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    zsh: segmentation fault (core dumped)  ./hebi
    

    This PR should fix that.

    opened by grtcdr 2
  • Spawn functions clunkiness

    Spawn functions clunkiness

    You might have that feeling too:

    Currently, if any piece of code wants to spawn a snake, segment, or wall, the system needs to query for those resources and pass them along up to and including the call site. There are like 8 parameters each so it is very verbose, and if you ever need to add something else to the spawn logic (e.g. suppose you later want to add sprites), updating the call sites will be a chore.

    This can be substantially improved by either:

    • Implementing your own Command, fetching the dependencies from the world inside the write function. This would make the call site look sorta like this:
      commands.add(SnakeSpawnCommand);
      

      Or even in this specific case, move the spawn-location-picking logic to the actual respawn system, and have the command take parameters:

      commands.add(SnakeSpawnCommand { x: 4, y: 6, direction: Direction::Up });
      
    • Using an Event, and a late-running system that listens to it to do the actual spawning. Register an Event type, and make a system that runs as late as possible in the frame that reads the events and the required dependencies, spawning the entities. The call site would then look like:
      snake_spawn_event_writer.send(SnakeSpawnEvent { x: 4, y: 6, direction: Direction::Up });
      
    refactoring 
    opened by kroltan 2
  • Move map generators to separate executables

    Move map generators to separate executables

    Moving each map generator (box, corridors, etc.) into a separate executable and then calling them from the main application instead of having them hard-coded in would increase customization potential for players, who could then create their own map generators for the game.

    enhancement 
    opened by ElnuDev 1
  • Refactor configuration loading and struct definitions

    Refactor configuration loading and struct definitions

    Instead of using the reasonably-verbose Serde default attributes, consider using the Default trait.

    Pros

    • Works across the ecosystem, since Default is from the Rust core library;
    • Slightly less verbose, you don't have to define a function for every single field;
    • Allows you to generate a completely default value without relying on Serde, which I've done in main.
      • This means that the "missing config file" handling is a bit cleaner, since it can skip summoning Serde if the file doesn't exist.

    Cons

    • Technically it's a wee bit more allocation-happy since all defaults are generated at once and then replaced one by one by the values from the config file. But a few string allocations during startup is not the end of the world.
      • I haven't done this, but it could be fixed by making the configs non-owning, and using &str instead of String, that would mean holding onto the whole source string for the lifetime of the program though. Less allocations, but more memory usage (though since the config file isn't huge it's not a big deal either)
    refactoring 
    opened by kroltan 1
  • Add ability to create custom input schemes

    Add ability to create custom input schemes

    With the merging of #15 and c52b37a, Hebi supports four input schemes: the original arrow keys, WASD, HJKL, and the number pad. It would be nice if the user could create custom input schemes in the configuration file.

    enhancement 
    opened by ElnuDev 0
  • Pressing multiple direction keys ignores valid inputs

    Pressing multiple direction keys ignores valid inputs

    For example, if I'm moving right, then hold the left arrow key, then press up, nothing happens. Since the current input code has a strict priority in the form of the if chain, if I hold a key, any key of a lower priority is ignored.

    Consider using key events instead of polling, so that the direction is always in sync with what the player last pressed.

    bug 
    opened by kroltan 0
  • Let map types be serialized directly

    Let map types be serialized directly

    Currently, the MapType trait cannot be serialized directly, since serde doesn't support serialization of traits (i.e. Box<dyn MapType>). As a work around, I'm using the Map enum with a variant for each individual map type. This works, but it's extremely verbose and requires a lot of duplicated code for each new map type.

    Apparently, the typetag crate will fix this problem—it adds the ability to serialize and deserialize trait objects to serde.

    enhancement 
    opened by ElnuDev 0
  • Add default values for map types

    Add default values for map types

    From the user's perspective, the way currently map values are passed in behaves differently than the rest of the config file: you have to put in every single map setting. There should be fallback values for each Map variant in a similar way to #[serde(default)] on Config.

    enhancement 
    opened by ElnuDev 0
  • Remove materials

    Remove materials

    Materials no longer seem necessary for sprites in Bevy 0.6, see the SpriteBundle and Sprite section in the migration guide. This should be done after migrating #25. For now, all material variables have been underscored to prevent unused variable warnings in b1184c7.

    refactoring 
    opened by ElnuDev 0
  • Allow audio variations

    Allow audio variations

    Nice project.

    If you are still looking for ideas you could implement you could allow the user to provide different audio variations for the same event. For example by allowing trailing numbers like so: eat_0.wav, eat_1.wav, eat_2.wav, etc. Quite a lot of games do that, so the sounds don't get to repetitive.

    opened by wischi-chr 0
  • Building with release flag hangs on MacOS 10.15.6

    Building with release flag hangs on MacOS 10.15.6

    These are the steps I followed:

    • Clone the repo
    • Install rust nightly
    • rustup show returns this:
    ~/hebi main
    ▲ rustup show
    Default host: x86_64-apple-darwin
    rustup home:  /Users/cog/.rustup
    
    installed toolchains
    --------------------
    
    stable-x86_64-apple-darwin (default)
    nightly-x86_64-apple-darwin
    
    active toolchain
    ----------------
    
    nightly-x86_64-apple-darwin (directory override for '/Users/cog/hebi')
    rustc 1.57.0-nightly (c3c0f80d6 2021-09-14)
    
    • run cargo build --release --verbose
    • Cook an egg with my laptop fans
    • Watch as hebi hangs on 299/300. I tried waiting up to 7 minutes and it didn't get unstuck. This is the output, if anyone can understand it: Screen Shot 2021-09-16 at 11 16 26 AM

    Other notes: The build size is about 500MB. A lot better than 1.26GB, but far bigger than linux or windows builds.

    help wanted 
    opened by CarlyRaeJepsenStan 3
  • Decouple maps from config file

    Decouple maps from config file

    Kind of like how themes work currently. Name in config, load from a separate file.

    Bonus points, would be cool if it used something like curl to be able to load remote files too, which would facilitate sharing.

    In that case, it maybe would also make sense to be able to specify a seed for the random map generators? So people can share a specific map, not just the parameters.

    enhancement 
    opened by kroltan 0
Releases(v0.1.2)
Owner
Elnu
Elnu
Engine / framework for creating highly customizable and user modable action RPG's

Rust RPG Toolkit PLEASE NOTE: this in early and very heavy development. API is subject to constant change, as it has newly transitioned from being a g

Ole A. Sjo Fasting 58 Dec 5, 2022
Minecraft-esque voxel engine prototype made with the bevy game engine. Pending bevy 0.6 release to undergo a full rewrite.

vx_bevy A voxel engine prototype made using the Bevy game engine. Goals and features Very basic worldgen Animated chunk loading (ala cube world) Optim

Lucas Arriesse 125 Dec 31, 2022
A game of snake written in Rust using the Bevy game engine, targeting WebGL2

Snake using the Bevy Game Engine Prerequisites cargo install cargo-make Build and serve WASM version Set your local ip address in Makefile.toml (loca

Michael Dorst 0 Dec 26, 2021
Customizable Performance/Debug Overlay for Bevy UI

Customizable Performance/Debug Overlay for Bevy UI Sponsor me: GitHub Sponsors Bevy Compatibility: Bevy Version Plugin Version 0.14 0.3 0.13 0.2,0.1 T

null 104 Jul 25, 2024
Motion graphics creation tool in Bevy. (Highly inspired by Motion Canvas and Manim)

Bevy MotionGfx Bevy Motiongfx is a motion graphics creation tool in Bevy. It is highly inspired by Motion Canvas & Manim. Goal The goal of this tool i

Nixon 3 Nov 6, 2023
An attempt to create an easily customizable MMORPG game engine. Sky not included.

skyless Proof of concept of an easily customizable MMORPG game engine. Getting started Copy environment variables cp .env.example .env Start the engi

null 8 Nov 23, 2022
2d Endless Runner Game made with Bevy Game Engine

Cute-runner A 2d Endless Runner Game made with Bevy Game Engine. Table of contents Project Infos Usage Screenshots Disclaimer Project Infos Date: Sept

JoaoMarinho 2 Jul 15, 2022
A game made in one week for the Bevy engine's first game jam

¿Quien es el MechaBurro? An entry for the first Bevy game jam following the theme of "Unfair Advantage." It was made in one week using the wonderful B

mike 20 Dec 23, 2022
Rubik's cube made with bevy engine.

English Rubik's Cube 魔方 3阶魔方 随机打乱魔方 重置魔方 鼠标拖拽魔方旋转 游戏UI 相机视角控制(缩放、移动) WASM支持 在线游玩:点这里(电脑版Chrome/Firefox/Edge打开) 运行 本地运行 cargo run WASM运行 rustup target

Night's Watch Games 4 Dec 31, 2022
3D wechat jump-jump game made with bevy engine.

English jump-jump 微信跳一跳游戏 角色跳跃动画 角色和平台蓄力效果 角色蓄力粒子特效 角色摔落效果 相机跟随 自动生成平台 飘分效果 分数统计 游戏UI 游戏音效 WASM支持 在线游玩:点这里(电脑版Chrome/Firefox/Edge打开) 运行 本地运行 cargo run

Night's Watch Games 17 Feb 27, 2023
Bevy Simple Portals is a Bevy game engine plugin aimed to create portals.

Portals for Bevy Bevy Simple Portals is a Bevy game engine plugin aimed to create portals. Those portals are (for now) purely visual and can be used t

Sélène Amanita 11 May 28, 2023
Port of my AST3 asteroids clone Bevy/rust from Unity/C#.

Port of my AST3 asteroids clone Bevy/rust from Unity/C#. This is my first rust program. Be gentle. Still working on it, but actually playable. You can

Jorge Monasterio 2 Mar 24, 2022
Clone of Soldat engine written in Rust

Soldank ?? open source clone of Soldat engine written in Rust ?? Screenshot Goals Fully authentic look and feel bugs feature-complete port of Soldat b

Tomasz Sterna 48 Dec 5, 2022
Naive and quick Wordle optimal starting word Analysis.

wordlentropy Naive and quick Wordle optimal starting word Analysis. This Rust code can analyze all 2315 Wordle games with 10657 word choices in 100 mi

Mufeed VH 2 Feb 7, 2022
A reimplementation of the excellent word game Wordle by Josh Wardle.

Paudle A reimplementation of the excellent word game Wordle by Josh Wardle. This version was created using Yew and Rust. I cribbed the colors and layo

Paul Sanford 39 Dec 5, 2022
bootable x86 snake game in rust

SnakeOS This is the game snake, bootable on a x86_64 CPU, written completely in rust. Its foundation is following the very interesting Writing an OS i

Tino Rusch 328 Dec 28, 2022
Classic snake in RUST!

rusty_snake Classic snake game built in Rust using Piston Window. Running the game Requirements Rust Cargo Steps to run git clone https://github.com/c

Dev Sharma 2 Nov 3, 2021
Reimplement the classical Snake game in Rust

Reimplement the classical Snake game in Rust Build cargo build Run cargo run Feedback All kinds of feedback are welcome! Feel free to give me any advi

null 1 Jan 25, 2022
Terminal-based Snake game written in Rust without dependencies (for educational purposes).

RustSnake This is a simple terminal-based Snake game implemented in Rust without dependencies. To start the game type cargo run. Control the snake usi

Florian Wickert 88 Jan 6, 2023