Skip to content
Snippets Groups Projects
Verified Commit 0bf83938 authored by Louis's avatar Louis :fire:
Browse files

Copy Template

parents
No related branches found
No related tags found
No related merge requests found
# Add the contents of this file to `config.toml` to enable "fast build" configuration. Please read the notes below.
# NOTE: For maximum performance, build using a nightly compiler
# If you are using rust stable, remove the "-Zshare-generics=y" below.
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"]
# NOTE: you must manually install https://github.com/michaeleisel/zld on mac. you can easily do this with the "brew" package manager:
# `brew install michaeleisel/zld/zld`
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/zld", "-Zshare-generics=y"]
[target.x86_64-pc-windows-msvc]
linker = "rust-lld.exe"
rustflags = ["-Zshare-generics=n"]
# Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only'
# In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains.
#[profile.dev]
#debug = 1
[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"
\ No newline at end of file
dist/
/target/
/.idea/
\ No newline at end of file
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
# Generated by Cargo
# will have compiled files and executables
/target/
# These are backup files generated by rustfmt
**/*.rs.bk
.idea/
.vscode/
dist/
\ No newline at end of file
image: "r.lcr.gr/microhacks/bevy-builder:latest"
variables:
BINARY_FOLDER: game_core
BINARY_NAME: game_core
stages:
- build
- package
build-windows:
stage: build
before_script:
- export CARGO_HOME="${CI_PROJECT_DIR}/.cargo"
- export PATH="${CI_PROJECT_DIR}/.cargo/bin:$PATH"
cache:
key: build-cache-windows
paths:
- .cargo/registry/cache
- .cargo/registry/index
- .cargo/git/db
- .cargo/bin/
- target/
script:
- cargo build --release -p ${BINARY_FOLDER} --target x86_64-pc-windows-gnu
artifacts:
expire_in: 1 day
paths:
- target/x86_64-pc-windows-gnu/release/${BINARY_NAME}.exe
only:
- trunk
build-linux:
stage: build
before_script:
- export CARGO_HOME="${CI_PROJECT_DIR}/.cargo"
- export PATH="${CI_PROJECT_DIR}/.cargo/bin:$PATH"
cache:
key: build-cache-linux
paths:
- .cargo/registry/cache
- .cargo/registry/index
- .cargo/git/db
- .cargo/bin/
- target/
script:
- cargo build --release -p ${BINARY_FOLDER} --target x86_64-unknown-linux-gnu
artifacts:
expire_in: 1 day
paths:
- target/x86_64-unknown-linux-gnu/release/${BINARY_NAME}
only:
- trunk
build-arm64:
image: "r.lcr.gr/microhacks/bevy-builder:arm64"
tags:
- arm64
stage: build
before_script:
- export CARGO_HOME="${CI_PROJECT_DIR}/.cargo"
- export PATH="${CI_PROJECT_DIR}/.cargo/bin:$PATH"
cache:
key: build-cache-arm64
paths:
- .cargo/registry/cache
- .cargo/registry/index
- .cargo/git/db
- .cargo/bin/
- target/
script:
- cargo build --release -p ${BINARY_FOLDER} --target aarch64-unknown-linux-gnu
artifacts:
expire_in: 1 day
paths:
- target/aarch64-unknown-linux-gnu/release/${BINARY_NAME}
only:
- trunk
build-web:
stage: build
before_script:
- export CARGO_HOME="${CI_PROJECT_DIR}/.cargo"
- export PATH="${CI_PROJECT_DIR}/.cargo/bin:$PATH"
cache:
key: build-cache-web
paths:
- .cargo/registry/cache
- .cargo/registry/index
- .cargo/git/db
- .cargo/bin/
- target/
script:
- mkdir -p "${CI_PROJECT_DIR}/${BINARY_FOLDER}/fonts"
- mkdir "${CI_PROJECT_DIR}/${BINARY_FOLDER}/dist"
- make assets
- sed -i 's#public_url = "/"#public_url = "./"#' "${CI_PROJECT_DIR}/${BINARY_FOLDER}/Trunk.toml"
- cd "${CI_PROJECT_DIR}/${BINARY_FOLDER}" && trunk build --release
- cd "${CI_PROJECT_DIR}"
artifacts:
expire_in: 1 day
paths:
- ${BINARY_FOLDER}/dist/
only:
- trunk
package-all:
stage: package
script:
- mkdir -p dist/
- make assets
- cp -r assets dist/assets
- cp target/x86_64-unknown-linux-gnu/release/${BINARY_NAME} "dist/${BINARY_NAME}"
- cp target/x86_64-pc-windows-gnu/release/${BINARY_NAME}.exe "dist/${BINARY_NAME}.exe"
- cp target/aarch64-unknown-linux-gnu/release/${BINARY_NAME} "dist/${BINARY_NAME}.arm64"
- cd "${CI_PROJECT_DIR}/dist" && zip -r "windows.zip" "./${BINARY_NAME}.exe" ./assets
- cd "${CI_PROJECT_DIR}/dist" && zip -r "linux.x86.zip" "./${BINARY_NAME}" ./assets
- cd "${CI_PROJECT_DIR}/dist" && zip -r "linux.arm64.zip" "./${BINARY_NAME}.arm64" ./assets
- cd "${CI_PROJECT_DIR}/${BINARY_FOLDER}/dist" && zip -r "web.zip" ./*
- cd "${CI_PROJECT_DIR}" && mv "${CI_PROJECT_DIR}/advent/dist/web.zip" "${CI_PROJECT_DIR}/dist/web.zip"
dependencies:
- build-windows
- build-linux
- build-arm64
- build-web
artifacts:
expire_in: 7 days
paths:
- dist/web.zip
- dist/windows.zip
- dist/linux.x86.zip
- dist/linux.arm64.zip
only:
- trunk
pages:
stage: package
script:
- mkdir -p public/
- cp -r ${BINARY_FOLDER}/dist/* public/
artifacts:
expire_in: 7 days
paths:
- public
dependencies:
- build-web
only:
- trunk
\ No newline at end of file
[Unreleased]
## Added
## Changed
## Removed
This diff is collapsed.
[workspace]
resolver = "2"
members = [
"game_core",
]
[profile.release]
debug = 0
opt-level = 3
Makefile 0 → 100644
PROJECT_NAME=game_core
CURRENT_DIRECTORY=$(shell pwd)
linux-deps:
sudo apt-get install -yyq clang pkg-config libx11-dev libasound-dev libudev-dev lld
wayland-deps:
sudo apt-get install -yyq libwayland-dev libxkbcommon-dev
cargo-deps:
rustup target install wasm32-unknown-unknown
cargo install -f wasm-server-runner
setup-x11: linux-deps cargo-deps
setup-wayland: linux-deps wayland-deps cargo-deps
# Update this command to build assets; commonly using Crunch to generate asset files
# If you remove this command, be sure to update .gitlab-ci.yml to remove the steps that
# call "make assets"
assets:
@echo "Beep Boop, No assets needed to be built"
run:
RUSTFLAGS="-Awarnings" \
cargo run --release --features "bevy/dynamic" -p game_core
run-web:
cd game_core && trunk serve --release
check:
cargo check --release --features "bevy/dynamic" -p game_core
build-windows: clean_dist top_tail
docker run --rm --name "${PROJECT_NAME}-build-windows" -v "$(CURRENT_DIRECTORY):/app" -w /app --user $(shell id -u):$(shell id -g) r.lcr.gr/microhacks/bevy-builder \
cargo build --release -p game_core --target x86_64-pc-windows-gnu
mkdir -p dist
cp -r assets dist/assets
cp target/x86_64-pc-windows-gnu/release/game_core.exe "dist/${PROJECT_NAME}.exe"
cd dist && zip -r "${PROJECT_NAME}-windows.zip" "./${PROJECT_NAME}.exe" ./assets
build-linux: clean_dist top_tail
docker run --rm --name "${PROJECT_NAME}-build-linux" -v "$(CURRENT_DIRECTORY):/app" -w /app --user $(shell id -u):$(shell id -g) r.lcr.gr/microhacks/bevy-builder \
cargo build --release -p game_core --target x86_64-unknown-linux-gnu
mkdir -p dist
cp -r assets dist/assets
cp target/x86_64-unknown-linux-gnu/release/game_core "dist/${PROJECT_NAME}"
cd dist && zip -r "${PROJECT_NAME}-linux.zip" "./${PROJECT_NAME}" ./assets
build-web: top_tail
cd game_core && trunk build --release
cd game_core/dist && zip -r "${PROJECT_NAME}-web.zip" ./*
clean_dist:
rm -rf ./dist
top_tail:
@echo "================================================"
@echo " Building ${PROJECT_NAME}"
@echo "================================================"
\ No newline at end of file
# Bevy 2D Template
## Includes
- Workspace configuration w/ assets dir
- Splash screen
- [Websockets](https://lab.lcr.gr/microhacks/micro-bevy-remote-events)
- [High level audio player](https://lab.lcr.gr/microhacks/micro-bevy-musicbox)
- Basic component based follow-camera setup
- Convenience loader for assets
- Asset loading state to preload all assets
- Gitlab CI template based on [Bevy Builder](https://lab.lcr.gr/microhacks/bevy-builder)
## Usage
Write some code, ya dig?
This project is set up for a multi-crate bevy game, but will work equally well
if just using a single crate for your game. `game_core` is the location of your
entry point, assets are placed at the root of the workspace in the `assets` folder.
Update the splash screen by replacing `assets/splash.png` - it is scaled by
default to fill the shortest screen side; you can include empty space in your
splash image as a simple way of changing the padding. `assets/splash_sting.mp3` will
be played during the splash screen.
If you need to pre-process files to generate your assets, update the `Makefile` `assets`
command so that Gitlab CI will package them properly.
Includes websocket based multiplayer building blocks, using [micro_bevy_remote_events](https://lab.lcr.gr/microhacks/micro-bevy-remote-events)
Comes with a Gitlab CI configuration that will build releases for web, Windows and Linux.
It will also publish a Gitlab pages site with the web version automatically.
## Commands
This project uses a Makefile to define some commands to get things going.
To run the game locally on linux, get things set up by running `make setup-x11` or `make setup-wayland`
depending on your requirements.
`make run` will run the desktop version of the game, while `make run-web` will run the game in your web
browser. The web version requires `trunk` to be installed (`cargo install trunk`).
Building the game for desktop will use a docker container to manage dependancies, but will mount
your workspace to perform the build. `make build-linux` and `make build-windows` can be run on any platform
with docker installed, perfect for cross-compilation without juggling dependencies. `make build-web`
## License
This template (and the code it contains) uses the GPL-3.0 license. This is unlikely to be changed in future on
a template-wide basis, but if you would like to discuss having this template licensed differently for a specific
project, please email [louis@microhacks.co.uk](mailto:louis@microhacks.co.uk).
The logo found in `assets/splash.png` is licensed under the following license for use only within the context of this template.
For clarity: Changing this splash screen asset is recommended, and a changed asset won't be subject to any licensing
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><span property="dct:title">Microhacks Logo</span> by <span property="cc:attributionName">Microhacks Ltd</span> is licensed under <a href="http://creativecommons.org/licenses/by-nc-nd/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-ND 4.0</a></p>
<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nd.svg?ref=chooser-v1">
assets/splash.png

131 B

File added
(function () {
// An array of all contexts to resume on the page
const audioContextList = [];
// An array of various user interaction events we should listen for
const userInputEventNames = [
'click',
'contextmenu',
'auxclick',
'dblclick',
'mousedown',
'mouseup',
'pointerup',
'touchend',
'keydown',
'keyup',
];
// A proxy object to intercept AudioContexts and
// add them to the array for tracking and resuming later
self.AudioContext = new Proxy(self.AudioContext, {
construct(target, args) {
const result = new target(...args);
audioContextList.push(result);
return result;
},
});
// To resume all AudioContexts being tracked
function resumeAllContexts(event) {
let count = 0;
audioContextList.forEach(context => {
if (context.state !== 'running') {
context.resume();
} else {
count++;
}
});
// If all the AudioContexts have now resumed then we
// unbind all the event listeners from the page to prevent
// unnecessary resume attempts
if (count == audioContextList.length) {
userInputEventNames.forEach(eventName => {
document.removeEventListener(eventName, resumeAllContexts);
});
}
}
// We bind the resume function for each user interaction
// event on the page
userInputEventNames.forEach(eventName => {
document.addEventListener(eventName, resumeAllContexts);
});
})();
\ No newline at end of file
document.addEventListener('DOMContentLoaded', function() {
if ('MutationObserver' in window) {
const config = {
attributes: false,
childList: true,
subtree: false,
}
function onElementAdded(mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
if (mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.nodeName.toLowerCase() === 'canvas') {
node.focus();
if (window.focusHandler) {
window.focusHandler.disconnect()
delete window.focusHandler
}
return
}
}
}
}
}
}
window.focusHandler = new MutationObserver(onElementAdded)
window.focusHandler.observe(document.body, config)
}
})
/target
[package]
name = "game_core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fastrand = "1.7"
anyhow = "1"
log = "0.4"
thiserror = "1"
serde = "1"
serde_json = "1"
iyes_loopless = "0.7"
musicbox = { git = "https://lab.lcr.gr/microhacks/micro-bevy-musicbox.git", rev = "5ab4120a983e54ef0786ddca43b92a70605efd49"}
remote_events = { git = "https://lab.lcr.gr/microhacks/micro-bevy-remote-events.git", rev = "be0c6b43a73e4c5e7ece20797e3d6f59340147b4"}
[dependencies.bevy]
version = "0.8"
default-features = false
features = [
"render",
"bevy_winit",
"png",
"hdr",
"x11",
]
[build]
public_url = "/"
[[hooks]]
stage = "post_build"
command = "sh"
command_arguments = ["-c", "flit -i $TRUNK_STAGING_DIR/index.html -f $TRUNK_SOURCE_DIR/flit.toml --inline"]
\ No newline at end of file
["script[type='module']"]
action = "wrap"
template ='''
<script>
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('start_button').addEventListener('click', function() {
let element = document.createElement('{{{TAG}}}');
let attr_string = `{{{ATTR}}}`
attr_string.split(' ').forEach(function(attr) {
let attr_array = attr.split('=');
element.setAttribute(attr_array[0], attr_array[1].slice(1, -1));
});
element.innerHTML = `{{{CONTENT}}}`;
document.body.appendChild(element);
const remove = ['start_container', 'init-styles']
remove.forEach(function(id) {
let container = document.getElementById(id);
container.parentNode.removeChild(container);
});
});
});
</script>'''
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Bevy 2D Template</title> <!-- TODO: Set game name -->
<link data-trunk rel="copy-dir" href="../assets"/>
<style>
html, body {
margin: 0;
padding: 0;
}
</style>
<style id="init-styles">
* {
transition: all 0.2s linear;
}
#start_container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
h1 {
font-family: sans-serif;
font-weight: bolder;
margin-top: 3rem;
margin-bottom: 3rem;
animation-name: float-anim;
animation-duration: 2.5s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
animation-direction: alternate;
}
@keyframes float-anim {
0% {
transform: translateY(10px);
}
100% {
transform: translateY(-10px);
}
}
#start_button {
width: 25%;
height: 25%;
background-color: black;
border-radius: 50%;
cursor: pointer;
}
.play-button-component {
fill: none;
stroke: #ffffff;
stroke-width: 10;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 4;
transform-origin: center;
}
.swircle {
stroke-dasharray: 100 50 100;
}
#start_button:hover .swircle:nth-of-type(2n) {
transform: rotateZ(-90deg);
}
#start_button:hover .swircle:first-of-type {
transform: rotateZ(90deg);
}
#start_button:hover {
background-color: rgba(0, 0, 0, 0.8);
}
</style>
</head>
<body>
<main id="start_container">
<svg
width="512"
height="512"
viewBox="0 0 512 512"
id="start_button"
xmlns="http://www.w3.org/2000/svg">
<path
class="play-button-component swircle"
d="M 482.86983,256 A 226.86983,226.86983 0 0 1 256,482.86983 226.86983,226.86983 0 0 1 29.130173,256 226.86983,226.86983 0 0 1 256,29.130173 226.86983,226.86983 0 0 1 482.86983,256 Z"/>
<path
class="play-button-component swircle"
d="M 447.06812,256 A 191.06812,191.06812 0 0 1 256,447.06812 191.06812,191.06812 0 0 1 64.931885,256 191.06812,191.06812 0 0 1 256,64.931885 191.06812,191.06812 0 0 1 447.06812,256 Z"/>
<path
class="play-button-component"
d="M 194.45831,348.47991 V 163.5198 l 159.08378,91.84706 -126.14795,72.83155 V 232.4989" />
</svg>
<h1>Press Start To Play</h1>
</main>
<link data-trunk rel="inline" href="../build/web/audio.js"/>
<link data-trunk rel="inline" href="../build/web/autofocus.js"/>
</body>
</html>
\ No newline at end of file
use std::marker::PhantomData;
use bevy::asset::LoadState;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use musicbox::prelude::AudioSource;
use crate::assets::{AssetHandles, FixedAssetNameMapping, SpriteSheetConfig};
#[derive(SystemParam)]
pub struct AssetTypeLoader<'w, 's> {
pub handles: ResMut<'w, AssetHandles>,
pub asset_server: Res<'w, AssetServer>,
pub atlas: ResMut<'w, Assets<TextureAtlas>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
macro_rules! load_basic_type {
($name: tt, $type: ty => $key: ident) => {
pub fn $name(&mut self, assets: &[FixedAssetNameMapping]) -> Vec<Handle<$type>> {
self.load_list(assets, |loader, path, key| {
let handle: Handle<$type> = loader.asset_server.load(&path);
loader.handles.$key.insert(key, handle.clone());
handle
})
}
};
}
macro_rules! load_state {
($container: expr => $key: ident) => {
$container
.asset_server
.get_group_load_state($container.handles.$key.values().map(|f| f.id))
};
}
impl<'w, 's> AssetTypeLoader<'w, 's> {
fn load_list<
T: Sync + Send + TypeUuid + 'static,
Loader: Fn(&mut AssetTypeLoader, String, String) -> Handle<T>,
>(
&mut self,
files: &[FixedAssetNameMapping],
load: Loader,
) -> Vec<Handle<T>> {
files
.iter()
.map(|(path, key)| load(self, path.to_string(), key.to_string()))
.collect()
}
load_basic_type!(load_images, Image => images);
load_basic_type!(load_audio, AudioSource => sounds);
load_basic_type!(load_font, Font => fonts);
pub fn load_spritesheet(
&mut self,
config: &SpriteSheetConfig,
assets: &[FixedAssetNameMapping],
) -> Vec<Handle<Image>> {
self.load_list(assets, |loader, path, key| {
let handle: Handle<Image> = loader.asset_server.load(&path);
loader
.handles
.images
.insert(key.to_string(), handle.clone());
let atlas = TextureAtlas::from_grid(
handle.clone(),
Vec2::new(config.tile_width as f32, config.tile_height as f32),
config.columns,
config.rows,
);
let atlas_handle = loader.atlas.add(atlas);
loader.handles.atlas.insert(key, atlas_handle);
handle
})
}
pub fn get_all_load_state(&self) -> Vec<LoadState> {
let image_state = self
.asset_server
.get_group_load_state(self.handles.images.values().map(|f| f.id));
let atlas_state = self
.asset_server
.get_group_load_state(self.handles.images.values().map(|f| f.id));
vec![image_state, atlas_state]
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment