Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/rust-ninja-sabi/rust-bevy-cheeseball

3D Game with Rust and Bevy. Using rapier
https://github.com/rust-ninja-sabi/rust-bevy-cheeseball

bevy-engine game-development rapier3d rust

Last synced: 6 days ago
JSON representation

3D Game with Rust and Bevy. Using rapier

Awesome Lists containing this project

README

        

{
"cells": [
{
"cell_type": "markdown",
"id": "f4169895-9110-4d6a-8552-3ee0d49d8270",
"metadata": {},
"source": [
"# bevy-cheeseball - creating a 3D game with rust and bevy"
]
},
{
"cell_type": "markdown",
"id": "e5363834-a363-4313-9ba8-73a15855c68a",
"metadata": {},
"source": [
"My third 3D game with rust(https://www.rust-lang.org) and the bevy framework(https://bevyengine.org)\n",
"using Rapier https://github.com/dimforge/bevy_rapier\n",
"\n",
"I am inspired by the classic marble games like #MonkeyBall. \n",
" \n",
"Thanks to Kenny https://www.kenney.nl for the assets."
]
},
{
"cell_type": "markdown",
"id": "7668f01d-8a35-4c17-bd2f-95285b0076eb",
"metadata": {},
"source": [
"## 1. Step _ setup first level"
]
},
{
"cell_type": "markdown",
"id": "753d113c-4908-4933-9803-4adcdfa9fd49",
"metadata": {},
"source": [
"Thanks to bevy_atmosphere - A procedural sky plugin for bevy https://github.com/JonahPlusPlus/bevy_atmosphere"
]
},
{
"cell_type": "markdown",
"id": "1a3ce75c-ed7c-483f-a295-716fc6205037",
"metadata": {},
"source": [
"













"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a476b64f-415a-4e9c-bf6a-36944542001b",
"metadata": {},
"outputs": [],
"source": [
"use bevy::prelude::*;\n",
"use bevy_atmosphere::*;\n",
"\n",
"\n",
"fn main() {\n",
" App::new()\n",
" //add config resources\n",
" .insert_resource(Msaa {samples: 4})\n",
" .insert_resource(WindowDescriptor{\n",
" title: \"cheeseball\".to_string(),\n",
" width: 800.0,\n",
" height: 600.0,\n",
" ..Default::default()\n",
" })\n",
" .insert_resource(bevy_atmosphere::AtmosphereMat::default())\n",
" //bevy itself\n",
" .add_plugins(DefaultPlugins)\n",
" .add_plugin(bevy_atmosphere::AtmospherePlugin {\n",
" dynamic: false,\n",
" sky_radius: 11.0,\n",
" })\n",
" // system once\n",
" .add_startup_system(setup)\n",
" // system frame\n",
" .run();\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7e25e703-14ff-42d5-974c-f09dde765129",
"metadata": {},
"outputs": [],
"source": [
"fn setup(\n",
" mut commands: Commands,\n",
" asset_server: Res,\n",
" mut meshes: ResMut>,\n",
" mut materials: ResMut>,\n",
") {\n",
" commands.spawn_bundle(PerspectiveCameraBundle{\n",
" transform: Transform::from_xyz(0.0,1.0,0.0).looking_at(Vec3::new(0.,0.,-4.), Vec3::Y),\n",
" ..Default::default()\n",
" });\n",
" \n",
" //light\n",
" const HALF_SIZE: f32 = 10.0;\n",
" commands.spawn_bundle(DirectionalLightBundle {\n",
" directional_light: DirectionalLight {\n",
" shadows_enabled: true,\n",
" ..default()\n",
" },\n",
" transform: Transform {\n",
" translation: Vec3::new(0.0, 4.0, 0.0),\n",
" rotation: Quat::from_rotation_x(-std::f32::consts::FRAC_PI_4),\n",
" ..default()\n",
" },\n",
" ..default()\n",
" });\n",
" \n",
" // ambient light\n",
" commands.insert_resource(AmbientLight {\n",
" color: Color::WHITE,\n",
" brightness: 0.02,\n",
" });\n",
" \n",
" //walls\n",
" let mut children_list:Vec = Vec::new();\n",
" let wall1 = commands\n",
" .spawn_bundle(PbrBundle {\n",
" mesh: meshes.add(Mesh::from(shape::Box::new(0.4,0.4,12.0))),\n",
" material: materials.add( StandardMaterial{\n",
" base_color: Color::rgb(0.5, 0.5, 0.5),\n",
" double_sided: true,\n",
" ..Default::default()\n",
" }),\n",
" transform: Transform {\n",
" translation: Vec3::new(-1.7, 0.2, 0.0),\n",
" rotation: Quat::from_rotation_x(0.0),\n",
" ..Default::default()\n",
" },\n",
" ..Default::default()\n",
" }).id();\n",
" children_list.push(wall1);\n",
" \n",
" let wall2 = commands\n",
" ... .id();\n",
" children_list.push(wall2);\n",
" let wall3 = commands\n",
" ... .id();\n",
" children_list.push(wall3);\n",
" \n",
" //door\n",
" let door1 = commands\n",
" ... .id();\n",
" children_list.push(door1);\n",
" let door2 = commands\n",
" ... .id();\n",
" children_list.push(door2);\n",
" let door3 = commands\n",
" ... .id();\n",
" children_list.push(door3);\n",
"\n",
" //platform\n",
" commands\n",
" .spawn_bundle(PbrBundle {\n",
" mesh: meshes.add(Mesh::from(shape::Box::new(3.0,0.1,12.0))),\n",
" material: materials.add( StandardMaterial{\n",
" base_color: Color::rgb(1.0, 0.8, 0.6),\n",
" double_sided: true,\n",
" ..Default::default()\n",
" }),\n",
" transform: Transform {\n",
" translation: Vec3::new(0.0, -2.0, -11.0),\n",
" rotation: Quat::from_rotation_x(0.0),\n",
" ..Default::default()\n",
" },\n",
" ..Default::default()\n",
" })\n",
" .push_children(&children_list);\n",
" \n",
" //cheese\n",
" let cheese_position = Vec3::new(0.0, -1.0, -8.0);\n",
"\n",
" commands.spawn_bundle((\n",
" Transform::from_translation(cheese_position),\n",
" GlobalTransform::identity(),\n",
" ))\n",
" .with_children(|parent| {\n",
" parent.spawn_scene(asset_server.load(\"models/cheese.glb#Scene0\"));\n",
" });\n",
" \n",
" //ball\n",
" commands\n",
" .spawn_bundle(PbrBundle {\n",
" mesh: meshes.add(Mesh::from(shape::UVSphere{\n",
" radius:0.5,\n",
" sectors:32,\n",
" stacks:32\n",
" })),\n",
" material: materials.add( StandardMaterial{\n",
" base_color: Color::rgb(0.0, 0.0, 1.0),\n",
" ..Default::default()\n",
" }),\n",
" transform: Transform {\n",
" translation: Vec3::new(0.0, -1.0, -5.0),\n",
" rotation: Quat::from_rotation_x(0.0),\n",
" ..Default::default()\n",
" },\n",
" ..Default::default()\n",
" });\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "da89fd8d-3d0b-473a-b7ef-b4dfdebf104a",
"metadata": {},
"source": [
"## 2. Step _ physics"
]
},
{
"cell_type": "markdown",
"id": "904be163-68ed-4c0b-b241-7e0541c3cb13",
"metadata": {},
"source": [
"using Rapier https://github.com/dimforge/bevy_rapier"
]
},
{
"cell_type": "markdown",
"id": "85797233-c46b-4760-b262-9ad6d6fc7ecf",
"metadata": {},
"source": [
"









"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4e1961ae-5ed6-4a47-9197-9f5e499b8fd4",
"metadata": {},
"outputs": [],
"source": [
"use bevy::prelude::*;\n",
"use bevy_atmosphere::*;\n",
"use bevy_rapier3d::prelude::*;\n",
"\n",
"fn main() {\n",
" App::new()\n",
" //add config resources\n",
" .insert_resource(Msaa {samples: 4})\n",
" .insert_resource(WindowDescriptor{\n",
" title: \"cheeseball\".to_string(),\n",
" width: 800.0,\n",
" height: 600.0,\n",
" ..Default::default()\n",
" })\n",
" .insert_resource(bevy_atmosphere::AtmosphereMat::default())\n",
" //bevy itself\n",
" .add_plugins(DefaultPlugins)\n",
" .add_plugin(bevy_atmosphere::AtmospherePlugin {\n",
" dynamic: false,\n",
" sky_radius: 11.0,\n",
" })\n",
" .add_plugin(RapierPhysicsPlugin::::default())\n",
" .add_plugin(RapierDebugRenderPlugin::default())\n",
" // system once\n",
" .add_startup_system(setup)\n",
" .run();\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "25ce8adb-294c-4803-996a-00443bd717df",
"metadata": {},
"outputs": [],
"source": [
"n setup(\n",
" ..\n",
") {\n",
" ..\n",
" //walls\n",
" let mut children_list:Vec = Vec::new();\n",
" let wall1 = commands\n",
" ..\n",
" })\n",
" .insert(Collider::cuboid(0.4/2.0, 0.4/2.0, 12.0/2.0))\n",
" .id();\n",
" children_list.push(wall1);\n",
" let wall2 = commands\n",
" ..\n",
" .insert(Collider::cuboid(0.4/2.0, 0.4/2.0, 12.0/2.0))\n",
" .id();\n",
" children_list.push(wall2);\n",
" let wall3 = commands\n",
" ..\n",
" .insert(Collider::cuboid(3.4/2.0, 0.4/2.0, 0.4/2.0))\n",
" .id();\n",
" children_list.push(wall3);\n",
" //door\n",
" let door1 = commands\n",
" ..\n",
" .insert(Collider::cuboid(2.0/2.0, 0.4/2.0, 0.4/2.0))\n",
" .id();\n",
" children_list.push(door1);\n",
" let door2 = commands\n",
" ..\n",
" .insert(Collider::cuboid(0.4/2.0, 1.2/2.0, 0.4/2.0))\n",
" .id();\n",
" children_list.push(door2);\n",
" let door3 = commands\n",
" ..\n",
" .insert(Collider::cuboid(0.4/2.0, 1.2/2.0, 0.4/2.0))\n",
" .id();\n",
" children_list.push(door3);\n",
"\n",
" //platform\n",
" commands\n",
" ..\n",
" .push_children(&children_list)\n",
" .insert(RigidBody::Fixed)\n",
" .insert(Sleeping::disabled())\n",
" .insert(Collider::cuboid(3.0/2.0, 0.1/2.0, 12.0/2.0));\n",
" //cheese\n",
" let cheese_position = Vec3::new(0.0, -1.0, -8.0);\n",
"\n",
" commands.spawn_bundle((\n",
" ..\n",
" .insert(RigidBody::Dynamic)\n",
" .insert(Sleeping::disabled())\n",
" .insert(Collider::cylinder(0.15, 0.3));;\n",
" //ball\n",
" commands\n",
" ..\n",
" .insert(RigidBody::Dynamic)\n",
" .insert(Sleeping::disabled())\n",
" .insert(Collider::ball(0.5));\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "ed749ca6-11a9-480d-b458-1e684ed1af7f",
"metadata": {},
"source": [
"## 3. Step _ move ball"
]
},
{
"cell_type": "markdown",
"id": "698c619b-93e2-48fc-b3ae-5019b43dfcde",
"metadata": {},
"source": [
"









"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "59816405-a3dd-4942-a6a3-949d8b6e99ac",
"metadata": {},
"outputs": [],
"source": [
"fn setup(\n",
" mut commands: Commands,\n",
" asset_server: Res,\n",
" mut meshes: ResMut>,\n",
" mut materials: ResMut>,\n",
") {\n",
" ..\n",
" //ball\n",
" commands\n",
" .spawn_bundle(PbrBundle {\n",
" ..\n",
" })\n",
" .insert(RigidBody::Dynamic)\n",
" .insert(Sleeping::disabled())\n",
" .insert(Collider::ball(0.5))\n",
" .insert(ExternalForce {\n",
" ..Default::default()\n",
" });\n",
"\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b6122b44-ff64-4600-bbcc-62f3f460d808",
"metadata": {},
"outputs": [],
"source": [
"const SPEED:f32= 1.0;\n",
"\n",
"fn input_user(\n",
" keyboard_input:Res>,\n",
" mut query_forces: Query<&mut ExternalForce>,\n",
"){\n",
"\n",
" let x = if keyboard_input.pressed(KeyCode::Left) {\n",
" -SPEED\n",
" } else if keyboard_input.pressed(KeyCode::Right) {\n",
" SPEED\n",
" } else {\n",
" 0.0\n",
" };\n",
"\n",
" let z = if keyboard_input.pressed(KeyCode::Up) {\n",
" -SPEED\n",
" } else if keyboard_input.pressed(KeyCode::Down) {\n",
" SPEED\n",
" } else {\n",
" 0.0\n",
" };\n",
"\n",
" if x != 0.0 || z != 0.0 {\n",
" for mut ext_force in query_forces.iter_mut() {\n",
" ext_force.force = Vec3::new(x,0.0, z);\n",
" }\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "6a89ec1e-903a-4ae6-ad81-17b3b2aebef9",
"metadata": {},
"source": [
"## 4. Step _ display"
]
},
{
"cell_type": "markdown",
"id": "615d7005-8ad0-49e4-ba86-cc8d0a3ca714",
"metadata": {},
"source": [
"









\n",
"

"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "184a3456-48fa-41a5-afcc-ab15a877d651",
"metadata": {},
"outputs": [],
"source": [
"struct Score {\n",
" cheese:i32,\n",
" level:i32\n",
"}\n",
"impl Default for Score{\n",
" fn default() -> Self {\n",
" Self {\n",
" cheese:0,\n",
" level:1,\n",
" }\n",
" }\n",
"}\n",
"\n",
"#[derive(Component)]\n",
"struct Cheesetext;\n",
"\n",
"#[derive(Component)]\n",
"struct Leveltext;"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "42114c59-7759-4585-b58a-c7522f732583",
"metadata": {},
"outputs": [],
"source": [
"fn main() {\n",
" App::new()\n",
" ..\n",
" .insert_resource(Score::default())\n",
" ..\n",
" .add_system(scoreboard)\n",
" .run();\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2481aeb3-5352-41ac-8078-42a005799d16",
"metadata": {},
"outputs": [],
"source": [
"fn scoreboard(\n",
" score: Res,\n",
" mut cheese_query: Query<(&mut Text, With, Without)>,\n",
" mut level_query: Query<&mut Text, With>,\n",
") {\n",
" let (mut text,_,_) = cheese_query.single_mut();\n",
" text.sections[0].value = format!(\"Cheese: {}\", score.cheese);\n",
"\n",
" let mut level_text = level_query.single_mut();\n",
" level_text.sections[0].value = format!(\"Level: {}\", score.level);\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "6988c403-80fe-4a9a-8428-f25fabb9bee7",
"metadata": {},
"source": [
"## 5. Step _ get cheese"
]
},
{
"cell_type": "markdown",
"id": "14b81e80-5d04-433d-9227-c4e4a9386296",
"metadata": {},
"source": [
"









"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3312b101-a8dd-43f6-ba9c-3cbbd84df7de",
"metadata": {},
"outputs": [],
"source": [
"fn setup(\n",
" ..\n",
") {\n",
" ..\n",
" //cheese\n",
"\n",
" commands.spawn_bundle((\n",
" ..\n",
" .insert(ActiveEvents::COLLISION_EVENTS)\n",
" .insert(Cheese{});\n",
" //ball\n",
" commands\n",
" .spawn_bundle(PbrBundle {\n",
" ..\n",
" .insert(ActiveEvents::COLLISION_EVENTS)\n",
" .insert(Ball{});\n",
"\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7dd5d7f-94a6-470d-90a4-86bce16d1840",
"metadata": {},
"outputs": [],
"source": [
"fn collision(\n",
" mut collision_events: EventReader,\n",
" mut score: ResMut,\n",
" query_ball: Query>,\n",
" query_cheese: Query>,\n",
" mut commands: Commands\n",
"){\n",
" let entity_ball = query_ball.single();\n",
" for e in collision_events.iter(){\n",
" //println!(\"{:?}\",e);\n",
" match e {\n",
" CollisionEvent::Started(e1,e2,_) => {\n",
" if e1 == &entity_ball || e2 == &entity_ball {\n",
" for entity_cheese in query_cheese.iter(){\n",
" if e1 == &entity_cheese || e2 == &entity_cheese {\n",
" commands.entity(entity_cheese).despawn_recursive();\n",
" score.cheese += 1;\n",
" }\n",
" }\n",
" }\n",
" }\n",
" CollisionEvent::Stopped(_,_,_)=> {}\n",
" }\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "b0b6c38c-4a1c-4ce7-a045-135e434c3f99",
"metadata": {},
"source": [
"## 6. Step _ collision effect"
]
},
{
"cell_type": "markdown",
"id": "09220bd3-808c-42f7-8770-ed41f0f50e44",
"metadata": {},
"source": [
"









"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c2aec18d-105f-42ca-ac6e-2324a63ef4ff",
"metadata": {},
"outputs": [],
"source": [
"struct CreateEffectEvent(Vec3);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7dd1f86f-fdd9-48b3-90d7-cda262b87271",
"metadata": {},
"outputs": [],
"source": [
"fn main() {\n",
" App::new()\n",
" ..\n",
" .add_event::()\n",
" ..\n",
" .run();\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6cfc9fe2-924b-429a-b71b-b42aaca2dbc7",
"metadata": {},
"outputs": [],
"source": [
"const EFFECT_SIZE:f32=0.1;\n",
"const EFFECT_TIME:f32=2.0;\n",
"\n",
"fn create_effect(\n",
" mut commands: Commands,\n",
" mut event_create_effect: EventReader,\n",
" mut meshes: ResMut>,\n",
" mut materials: ResMut>,\n",
")\n",
"{\n",
" let mut rng = rand::thread_rng();\n",
" for event in event_create_effect.iter() {\n",
" let pos = event.0;\n",
" for x in -2..2 {\n",
" for y in 0..2 {\n",
" for z in -2..2 {\n",
" commands\n",
" .spawn_bundle(PbrBundle {\n",
" mesh: meshes.add(Mesh::from(shape::Box::new(0.1, 0.1, 0.1))),\n",
" material: materials.add(StandardMaterial {\n",
" metallic: 0.5,\n",
" emissive: Color::rgb(1.0, 0.5, 0.0),\n",
" ..Default::default()\n",
" }),\n",
" transform: Transform {\n",
" translation: Vec3::new(x as f32 * EFFECT_SIZE+pos.x,\n",
" y as f32 * EFFECT_SIZE+pos.y,\n",
" z as f32 * EFFECT_SIZE+pos.z),\n",
" rotation: Quat::from_rotation_x(0.0),\n",
" ..Default::default()\n",
" },\n",
" ..Default::default()\n",
" })\n",
" .insert(RigidBody::Dynamic)\n",
" .insert(ExternalImpulse {\n",
" impulse: Vec3::new(rng.gen_range(-0.01..0.01),\n",
" 0.01,\n",
" rng.gen_range(-0.01..0.01)),\n",
" torque_impulse: Vec3::new(0.0, 0.0, 0.0),\n",
" })\n",
" .insert(Timer{value:EFFECT_TIME})\n",
" .insert(Sleeping::disabled())\n",
" .insert(Collider::cuboid(0.1 / 2.0, 0.1 / 2.0, 0.1 / 2.0));\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"fn remove_effect(\n",
" mut commands: Commands,\n",
" time:Res