├── game ├── main.js ├── img │ └── texture.png ├── src │ ├── game-object.js │ ├── constants.js │ ├── game.js │ ├── hud.js │ ├── scene.js │ ├── state-manager.js │ ├── barrier.js │ ├── player.js │ └── mainmenu.js └── index.html ├── .gitignore ├── .prettierrc ├── .github ├── issues │ ├── greetings-issue.md │ ├── high-scoring-issue.md │ ├── font-issue.md │ ├── scoring-issue.md │ ├── pause-issue.md │ ├── art-design-issue.md │ ├── post-workshop.md │ ├── config-service-issue.md │ └── docs-issue.md └── workflows │ ├── gh-pages.yml │ └── create-initial-issues.yml ├── .vscode └── launch.json ├── LICENSE ├── .devcontainer └── devcontainer.json └── README.md /game/main.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /game/img/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcusOtter/CubeFlyer/HEAD/game/img/texture.png -------------------------------------------------------------------------------- /.github/issues/greetings-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Add more greetings 3 | labels: enhancement 4 | --- 5 | 6 | There are a number of greetings in the `Constants` file. Lets add a few more to introduce some extra variety to the game. 7 | -------------------------------------------------------------------------------- /game/src/game-object.js: -------------------------------------------------------------------------------- 1 | class GameObject { 2 | constructor() { 3 | this.destroyed = false; 4 | } 5 | 6 | init() {} 7 | 8 | destroy() { 9 | this.destroyed = true; 10 | } 11 | 12 | onDestroy() {} 13 | 14 | update(deltaTime) {} 15 | } 16 | -------------------------------------------------------------------------------- /game/src/constants.js: -------------------------------------------------------------------------------- 1 | const gravity = new BABYLON.Vector2(0, -9.8); 2 | const flightForce = 5; // The amount of force applied when the "fly" button is pressed 3 | 4 | const greetings = ["It's a bird!", "It's a plane!", "1000% more Cube!", "A Whole New Dimension!"]; 5 | -------------------------------------------------------------------------------- /.github/issues/high-scoring-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Add High Score View 3 | labels: enhancement 4 | --- 5 | 6 | Add some way to track the High Score over a session that keeps track of how high a player has scored in the hud file and displays the maximum score that anyone has achieved during that play. 7 | -------------------------------------------------------------------------------- /.github/issues/font-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Change the Score Font to Something Not Comic Sans 3 | labels: enhancement 4 | --- 5 | 6 | The scoreboard is currently being drawn with the comic sans font... 7 | 8 | That's configured in hud.js. 9 | 10 | ![](https://user-images.githubusercontent.com/1161329/184423685-8d31ad1c-b4fd-4dc3-9304-beb154df3577.png) 11 | 12 | Can we change this to something else? Maybe `parchment`? 13 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 🛎️ 15 | uses: actions/checkout@v3 16 | 17 | - name: Deploy 🚀 18 | uses: JamesIves/github-pages-deploy-action@v4 19 | with: 20 | folder: game 21 | -------------------------------------------------------------------------------- /.github/issues/scoring-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Support Player Scoring 3 | labels: enhancement 4 | --- 5 | 6 | Add the ability to score in the game. The hud file already supports displaying the score but there isn't any way to actually score anything yet. 7 | 8 | Let's find a way to earn points and track that! 9 | 10 | Some ideas: 11 | 12 | - Score points the longer you are able to play. 13 | - Score points when an obstacle is past the player (or created) 14 | - Add coins that can be picked up to earn points 15 | - Something else? 16 | -------------------------------------------------------------------------------- /.github/issues/pause-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Need a pause between hitting an object and being able to restart 3 | labels: bug 4 | --- 5 | 6 | Not sure if this is a bug report or a feature request. 7 | 8 | **To Reproduce** 9 | 10 | 1. Play the game, tapping space to stay flying 11 | 2. Collide with an obstacle just as you tap space to fly 12 | 3. The tapping of space just after the game ends quickly restarts the game, not giving you time to realize you hit an object 13 | 14 | **Expected behavior** 15 | The game should obviously end and give you time to realize before you restart 16 | -------------------------------------------------------------------------------- /.github/issues/art-design-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Art and design 3 | labels: enhancement 4 | --- 5 | 6 | The CubeFlyer game has graphics that are somewhat basic - it is a cube flying through blocks. This can be improved, so those attendees with an art or design background can help make both the UI and the game graphics better. 7 | 8 | An a **user interface** team, you have been tasked with: 9 | 10 | - Changing the cube for a different object 11 | - Improving the look of the columns 12 | - Making general improvements to the user interface such as color schemes, fonts, or the layout 13 | -------------------------------------------------------------------------------- /game/src/game.js: -------------------------------------------------------------------------------- 1 | //import { createScene } from 'scene.js'; 2 | 3 | const canvas = document.getElementById("renderCanvas"); // Get the canvas element 4 | const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine 5 | 6 | const scene = createScene(); //Call the createScene function from scene.js 7 | 8 | // Register a render loop to repeatedly render the scene 9 | engine.runRenderLoop(function () { 10 | scene.render(); 11 | }); 12 | 13 | scene.registerBeforeRender(function () { 14 | updateGame(); 15 | }); 16 | 17 | // Watch for browser/canvas resize events 18 | window.addEventListener("resize", function () { 19 | engine.resize(); 20 | }); 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-msedge", 9 | "request": "launch", 10 | "name": "Launch Edge against localhost", 11 | "file": "${workspaceFolder}/game/index.html", 12 | "webRoot": "${workspaceFolder}" 13 | }, 14 | { 15 | "type": "chrome", 16 | "request": "launch", 17 | "name": "Launch Chrome against localhost", 18 | "file": "${workspaceFolder}/game/index.html", 19 | "webRoot": "${workspaceFolder}" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.github/issues/post-workshop.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post Workshop 3 | labels: ["good first issue"] 4 | --- 5 | 6 | ## You've made it throught the workshop, well done! 7 | 8 | This issue includes some information about next steps that can be taken throughout Global Hack Week: Games. 9 | 10 | ## Itch.io 11 | 12 | [Itch.io](https://itch.io/) is a site that contains many free game assets that you can use in your game. Feel free to explore it! Many other game jams are hosted on Itch.io, so if you want to continue trying some game development after the Global Hack Week, be sure to check them out! 13 | 14 | Link: https://itch.io/ 15 | 16 | ## GitHub Student Developer Pack 17 | 18 | If you're a student, make sure you sign up for GitHub's Student Developer Pack! This will give you access to a lot of tools, such as GitHub Copilot, and extra time on GitHub Codespaces. There's a ton more in it, so make sure you don't miss out! 19 | 20 | Sign up here: https://education.github.com/pack -------------------------------------------------------------------------------- /game/src/hud.js: -------------------------------------------------------------------------------- 1 | var score = 0; 2 | var scoreText; 3 | 4 | var createHud = function () { 5 | var hudTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI"); 6 | 7 | // Create a Text Block that can display the current score 8 | scoreText = new BABYLON.GUI.TextBlock(); 9 | scoreText.fontFamily = "Comic Sans, Comic Sans MS"; 10 | scoreText.color = "white"; 11 | scoreText.fontSize = 48; 12 | scoreText.verticalAlignment = BABYLON.GUI.TextBlock.VERTICAL_ALIGNMENT_TOP; 13 | scoreText.horizontalAlignment = BABYLON.GUI.TextBlock.HORIZONTAL_ALIGNMENT_CENTER; 14 | scoreText.width = 0.5; 15 | scoreText.height = 0.15; 16 | 17 | updateScoreText(); 18 | 19 | hudTexture.addControl(scoreText); 20 | }; 21 | 22 | var updateScoreText = function () { 23 | scoreText.text = "Score: " + score; 24 | }; 25 | 26 | var resetScore = function () { 27 | console.log("Score reset at: " + score); 28 | score = 0; 29 | updateScoreText(); 30 | }; 31 | 32 | var addScore = function (points) { 33 | score += points; 34 | updateScoreText(); 35 | }; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Open Source Bootcamp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/javascript-node 3 | { 4 | "name": "Node.js", 5 | "image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12", 6 | 7 | // Configure tool-specific properties. 8 | "customizations": { 9 | // Configure properties specific to VS Code. 10 | "vscode": { 11 | // Add the IDs of extensions you want installed when the container is created. 12 | "extensions": [ 13 | "dbaeumer.vscode-eslint", 14 | "visualstudioexptteam.vsc", 15 | "redhat.vscode-yaml", 16 | "ritwickdey.LiveServer", 17 | "MS-vsliveshare.vsliveshare", 18 | "esbenp.prettier-vscode" 19 | ] 20 | } 21 | }, 22 | 23 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 24 | // "forwardPorts": [], 25 | 26 | // Use 'postCreateCommand' to run commands after the container is created. 27 | // "postCreateCommand": "yarn install", 28 | 29 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 30 | "remoteUser": "node" 31 | } 32 | -------------------------------------------------------------------------------- /game/src/scene.js: -------------------------------------------------------------------------------- 1 | const gameHeight = 5; // Defines the height of the floor and ceiling in the game. 2 | 3 | var camera; 4 | 5 | var createScene = function () { 6 | // This creates a basic Babylon Scene object (non-mesh) 7 | var scene = new BABYLON.Scene(engine); 8 | 9 | // This creates and positions a free camera (non-mesh) 10 | camera = new BABYLON.TargetCamera("camera1", new BABYLON.Vector3(0, 0, -15), scene); 11 | 12 | // This targets the camera to scene origin (where the player is) 13 | camera.setTarget(BABYLON.Vector3.Zero()); 14 | 15 | // This attaches the camera to the canvas 16 | camera.attachControl(canvas, true); 17 | 18 | // This creates a light, aiming 0,1,0 - to the sky (non-mesh) 19 | var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0.3, 0.7, -0.3), scene); 20 | 21 | // Default intensity is 1. Let's dim the light a small amount 22 | light.intensity = 0.7; 23 | 24 | // Add a ground and ceiling object 25 | var ground = BABYLON.MeshBuilder.CreateBox("ground", { width: 100, depth: 6, height: 1 }, scene); 26 | var ceiling = BABYLON.MeshBuilder.CreateBox("ceiling", { width: 100, depth: 6, height: 1 }, scene); 27 | ground.position.y = -gameHeight - 0.5; // +/- 0.5 to account for height of the cubes 28 | ceiling.position.y = gameHeight + 0.5; 29 | 30 | createHud(); 31 | 32 | return scene; 33 | }; 34 | 35 | var updateGame = function () { 36 | updateGameState(); 37 | }; 38 | -------------------------------------------------------------------------------- /.github/issues/config-service-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Add a configuration service 3 | labels: enhancement 4 | --- 5 | 6 | The game is fun with the current configuration of greetings and gravity, and it may have a local high score if this was implemented by teams in day 1. It would be nicer if this could all be in a service running somewhere that would allow the configuration to be updated without having to change the game code, or for high scores to be persisted. 7 | 8 | > A lot of software does this - it has certain values set from a configuration service that can be tweaked without redeploying the application based on data gathered from users of the application. For example, in a game if a lot of users are failing to defeat a certain boss, the game can be tweaked through configuration to make the boss easier to beat. 9 | 10 | As a **software engineering** team, you have been tasked with: 11 | 12 | - Creating a service that hosts a web API that you can use to retrieve configuration every time the page is refreshed 13 | - Adding the ability to store high scores using this web API. 14 | 15 | How this is created is up to the team. It can be a local Python/Flask web app that stores the scores in memory, it can use a database to store the scores and configuration, use a cloud-based gaming service, or even a cloud-based serverless application. It depends on the skill level of the team, though the recommendation is to start as simple as possible. 16 | -------------------------------------------------------------------------------- /game/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Flying Cube Game 7 | 8 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /.github/issues/docs-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation 3 | labels: enhancement 4 | --- 5 | 6 | One of the most needed parts of open source projects is documentation. Many developers will want to work on code, but few want to work on docs. This is a shame, as docs are the most important part of an open source project for a new person to start using it. You can have the most perfect library, but it is useless if no-one knows how to use it. 7 | 8 | The CubeFlyer project is lacking in good documentation. 9 | 10 | As a **documentation** team, you have been tasked with actually creating documentation. This includes: 11 | 12 | - A markdown file with a guide to running the project locally 13 | - Documentation on the structure of the code, also as a markdown file 14 | - Documentation for the features being added by other teams 15 | 16 | This documentation should all be in markdown. To add documentation for features being added by other teams, you will need to collaborate with those teams to generate the documentation in tandem. This will be good learning for the other teams to help cement the idea that documentation is a continuous process that includes everyone. 17 | 18 | To help you learn markdown syntax, you should work through the [Communicate using Markdown lab on GitHub skills](https://github.com/skills/communicate-using-markdown). 19 | 20 | VS Code has great support for markdown built in, and its markdown functionality can be improved using a number of extensions. You can learn more in the [markdown in VS Code documentation](https://code.visualstudio.com/docs/languages/markdown). 21 | -------------------------------------------------------------------------------- /game/src/state-manager.js: -------------------------------------------------------------------------------- 1 | var currentScreen; // enum to track game state (main menu, playing) 2 | var newGameObjects = []; // collection of new game objects to be created at end of frame 3 | var currentGameObjects = []; // collection of all game objects in the scene 4 | 5 | var createObject = function (gameObject) { 6 | newGameObjects.push(gameObject); 7 | }; 8 | 9 | var destroyObject = function (gameObject) { 10 | gameObject.destroyed = true; 11 | }; 12 | 13 | var destroyMatchingObjects = function (predicate) { 14 | currentGameObjects.forEach((gameObject) => { 15 | if (predicate(gameObject)) { 16 | destroyObject(gameObject); 17 | } 18 | }); 19 | }; 20 | 21 | var testMatchingObjects = function (objectMatch, predicate) { 22 | var result = false; 23 | currentGameObjects.forEach((gameObject) => { 24 | if (objectMatch(gameObject)) { 25 | if (predicate(gameObject)) { 26 | result = true; 27 | } 28 | } 29 | }); 30 | return result; 31 | }; 32 | 33 | var updateGameState = function () { 34 | // Delta Time is used to track how much time has passed since the last update 35 | // We use this instead of a fixed value to account for changes in frame rate (lag) 36 | // This means the game will play similarly at different frame rates. 37 | let deltaTime = scene.deltaTime / 1000; 38 | 39 | currentGameObjects.forEach((gameObject) => { 40 | gameObject.update(deltaTime); 41 | }); 42 | 43 | // Object Creation Queue 44 | newGameObjects.forEach((gameObject) => { 45 | currentGameObjects.push(gameObject); 46 | gameObject.init(); // Initialize newly created objects 47 | }); 48 | 49 | for (let index = currentGameObjects.length - 1; index >= 0; index--) { 50 | if (currentGameObjects[index].destroyed) { 51 | currentGameObjects[index].onDestroy(); 52 | currentGameObjects.splice(index, 1); 53 | } 54 | } 55 | 56 | // Reset create/destroy sets 57 | newGameObjects = []; 58 | }; 59 | -------------------------------------------------------------------------------- /.github/workflows/create-initial-issues.yml: -------------------------------------------------------------------------------- 1 | name: Create initial issues when creating a repo from this template 2 | on: [create] 3 | permissions: 4 | contents: read 5 | issues: write 6 | jobs: 7 | create-issues: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: JasonEtco/create-an-issue@v2 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | with: 15 | filename: .github/issues/greetings-issue.md 16 | - uses: JasonEtco/create-an-issue@v2 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | with: 20 | filename: .github/issues/font-issue.md 21 | - uses: JasonEtco/create-an-issue@v2 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | with: 25 | filename: .github/issues/scoring-issue.md 26 | - uses: JasonEtco/create-an-issue@v2 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | with: 30 | filename: .github/issues/high-scoring-issue.md 31 | - uses: JasonEtco/create-an-issue@v2 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | with: 35 | filename: .github/issues/pause-issue.md 36 | - uses: JasonEtco/create-an-issue@v2 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | filename: .github/issues/art-design-issue.md 41 | - uses: JasonEtco/create-an-issue@v2 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | with: 45 | filename: .github/issues/config-service-issue.md 46 | - uses: JasonEtco/create-an-issue@v2 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | with: 50 | filename: .github/issues/docs-issue.md 51 | - uses: JasonEtco/create-an-issue@v2 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | with: 55 | filename: .github/issues/pm-issue.md 56 | - uses: JasonEtco/create-an-issue@v2 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | filename: .github/issues/post-workshop.md 61 | always_job: 62 | name: Always run job 63 | runs-on: ubuntu-latest 64 | steps: 65 | - name: Always run 66 | run: echo "This will always run to prevent notificaitons from GitHub, see https://github.com/orgs/community/discussions/25146" 67 | -------------------------------------------------------------------------------- /game/src/barrier.js: -------------------------------------------------------------------------------- 1 | var obstacleSpeed = 1.5; // Changing this will impact how quickly obstacles in the game move. 2 | var gapSize = 3; // This determines the size of the gap to create between the floor and ceiling. 3 | 4 | class Barrier extends GameObject { 5 | constructor() { 6 | super(); 7 | } 8 | 9 | init() { 10 | // This is hardcoded for now - should be some location off the right side of the screen 11 | this.location = 15; 12 | 13 | // Creates 2 boxes which will be used for the top and bottom obstacles, 14 | // the floor will obscure the height of the object so we don't need to modify this much. 15 | const boxOptions = { width: 1, height: 10, depth: 1 }; 16 | this.ceilingBox = BABYLON.MeshBuilder.CreateBox("ceilingObstacle", boxOptions, scene); 17 | this.floorBox = BABYLON.MeshBuilder.CreateBox("floorObstacle", boxOptions, scene); 18 | // Materials impact how an object is rendered like color, texture etc. 19 | let barrierMaterial = new BABYLON.StandardMaterial("Barrier Material", scene); 20 | barrierMaterial.diffuseColor = BABYLON.Color3.Green(); 21 | this.ceilingBox.material = barrierMaterial; 22 | this.floorBox.material = barrierMaterial; 23 | this.assignLocations(); 24 | } 25 | 26 | onDestroy() { 27 | // Remember when destroying an object to remove all meshes it creates from the scene! 28 | scene.removeMesh(this.ceilingBox); 29 | scene.removeMesh(this.floorBox); 30 | } 31 | 32 | update(deltaTime) { 33 | this.location -= deltaTime * obstacleSpeed; 34 | 35 | // Update the players physics: 36 | this.ceilingBox.position.x = this.location; 37 | this.floorBox.position.x = this.location; 38 | 39 | if (this.location < -25) { 40 | destroyObject(this); 41 | } 42 | } 43 | 44 | assignLocations() { 45 | // Pick a random center point 46 | let height = -gameHeight + gapSize / 2 + Math.random() * (gameHeight - gapSize / 2) * 2; 47 | this.ceilingBox.position.y = height + gapSize / 2 + 5; 48 | this.floorBox.position.y = height - gapSize / 2 - 5; 49 | this.ceilingBox.position.x = this.location; 50 | this.floorBox.position.x = this.location; 51 | } 52 | 53 | testCollision(playerHeight) { 54 | if (this.location > -1 && this.location < 1) { 55 | // In the same location as the player 56 | if ( 57 | playerHeight + 5.5 > this.ceilingBox.position.y || // 5.5 is the half the height of the box + half the height of the player 58 | playerHeight - 5.5 < this.floorBox.position.y 59 | ) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Flying Cube Game! 🟥💨 2 | 3 | Hi! 4 | 5 | This project is intended to help introduce you to Open Source as part of [MLH 2023 Global Hack Week: GAMES](https://organize.mlh.io/participants/events/9153-global-hack-week-game-week)! It's a JavaScript-based 3D game built with [Babylon.JS](https://github.com/BabylonJS) that lets you fly a Cube through a set of obstacles. 6 | 7 | When you create a repository from this template, a series of [Issues](https://docs.github.com/en/issues) will be generated that you can pick up to get started. 8 | 9 | The entire project can be run by opening [`game/index.html`](game/index.html) in a browser. All of the 3rd party dependencies like Babylon are using a CDN so you do not need Node or a local developer environment. 10 | 11 | ## 🧙‍ New Open Sourcerers 12 | If you're new to GitHub you can get started by creating a new repository by clicking `Use this template` and then selecting `Create a new repository` to create a copy of this repository into your own GitHub account. This is where you can make your own changes! 13 | 14 | ![The green button used to create a new repository from this template](https://user-images.githubusercontent.com/35617441/234130644-bb6295d7-0499-4c3b-9214-495956201b09.png) 15 | 16 | If you want to keep learning about GitHub you can check out [GitHub Skills](https://skills.github.com/). 17 | 18 | If you're new to Babylon JS that's fine too! Here are some helpful links to help you get started. Babylon runs in your browser and this game should work on your Phone, Tablet or PC - you can make and test your changes from any of those devices! If something doesn't work you can enter the developer mode of your browser (F12 on Edge or Chrome) and explore the errors in the console. This developer mode also lets you insert breakpoints if you want to debug further. 19 | 20 | - The [Babylon.JS Playground](https://playground.babylonjs.com/) will let you explore different community samples to learn how Babylon works! 21 | - The [Babylon.JS API](https://doc.babylonjs.com/typedoc/modules/BABYLON) has documentation that can help you understand the API. 22 | 23 | We've also provided a file that should support launching from VS Code that will allow you to integrate your debugging experience there as well as a [devcontainer](https://code.visualstudio.com/docs/remote/create-dev-container) you can use to develop in if you choose with some recommended extensions pre-installed. 24 | 25 | This project is intended as a sandbox for you to learn about GitHub and contributing to Open Source. 26 | 27 | ## Acknowledgements 28 | All the code in this repo was written by [Sam Wronski](https://github.com/runewake2). 29 | -------------------------------------------------------------------------------- /game/src/player.js: -------------------------------------------------------------------------------- 1 | var gamepadManager = new BABYLON.GamepadManager(); 2 | 3 | var deviceSourceManager; 4 | 5 | const obstacleSpawnInterval = 3.5; 6 | 7 | class Player extends GameObject { 8 | constructor() { 9 | super(); 10 | } 11 | 12 | init() { 13 | this.obstacleSpawnTimer = 0; 14 | // A Vector2 is a 2 dimensional vector with X and Y dimension - track velocity with this. 15 | this.velocity = new BABYLON.Vector3(0, 0); 16 | this.setupInputs(); 17 | 18 | // Create the player object - a 1 unit square cube 19 | const boxOptions = { width: 1, height: 1, depth: 1 }; 20 | this.playerMesh = BABYLON.MeshBuilder.CreateBox("bird", boxOptions, scene); 21 | this.playerMaterial = new BABYLON.StandardMaterial("Player Material", scene); 22 | this.playerMesh.material = this.playerMaterial; 23 | this.playerMesh.material.diffuseColor = BABYLON.Color3.White(); 24 | } 25 | 26 | onDestroy() { 27 | scene.removeMesh(this.playerMesh); 28 | } 29 | 30 | update(deltaTime) { 31 | // Update the players physics: 32 | this.velocity.y += gravity.y * deltaTime; 33 | this.capVelocity(20); 34 | this.playerMesh.position.y += this.velocity.y * deltaTime; 35 | if (this.testGameOver()) { 36 | this.endGame(); 37 | } 38 | 39 | // To simplify game code the Player handles spawning obstacles (this makes it easier to track for collisions without writing a full handler) 40 | // A side effect of this is that creating or destroying the Player can pause or start the game. 41 | this.obstacleSpawnTimer -= deltaTime; 42 | if (this.obstacleSpawnTimer <= 0) { 43 | this.obstacleSpawnTimer = obstacleSpawnInterval; 44 | 45 | createObject(new Barrier()); 46 | } 47 | } 48 | 49 | endGame() { 50 | // This is used to identify and remove barrier objects from the scene 51 | destroyMatchingObjects((gobj) => gobj.location !== undefined); 52 | 53 | mainMenu.visible = true; 54 | destroyObject(this); 55 | resetScore(); 56 | } 57 | 58 | testGameOver() { 59 | let outOfBounds = this.playerMesh.position.y > gameHeight || this.playerMesh.position.y < -gameHeight; 60 | 61 | let collision = testMatchingObjects( 62 | (gameObject) => gameObject.testCollision !== undefined, 63 | (gameObject) => gameObject.testCollision(this.playerMesh.position.y) 64 | ); 65 | 66 | if (collision) { 67 | console.log("IMPACT"); 68 | } 69 | 70 | return outOfBounds || collision; 71 | } 72 | 73 | onPlayerFlight() { 74 | this.velocity.y += flightForce; 75 | } 76 | 77 | capVelocity(cap) { 78 | this.velocity.y = Math.min(cap, Math.max(-cap, this.velocity.y)); 79 | } 80 | 81 | setupInputs() { 82 | deviceSourceManager = new BABYLON.DeviceSourceManager(scene.getEngine()); 83 | /** 84 | * onDeviceConnectedObservable is fired after a device is connected so any code that we 85 | * put in here should be able to reliably work against an existing device. 86 | * 87 | * For onInputChangedObservable, this will only work with Mouse, Touch, and Keyboards because 88 | * the Gamepad API currently does not fire input changed events (polling only) 89 | */ 90 | deviceSourceManager.onDeviceConnectedObservable.add((deviceSource) => { 91 | // If Mouse/Touch, add an Observer to change text 92 | if ( 93 | deviceSource.deviceType === BABYLON.DeviceType.Mouse || 94 | deviceSource.deviceType === BABYLON.DeviceType.Touch 95 | ) { 96 | deviceSource.onInputChangedObservable.add((eventData) => { 97 | if (eventData.type === "pointerdown" && eventData.inputIndex === BABYLON.PointerInput.LeftClick) { 98 | this.onPlayerFlight(); 99 | } 100 | }); 101 | } 102 | // If Keyboard, add an Observer to change text 103 | else if (deviceSource.deviceType === BABYLON.DeviceType.Keyboard) { 104 | deviceSource.onInputChangedObservable.add((eventData) => { 105 | if (eventData.type === "keydown" && eventData.key === " ") { 106 | this.onPlayerFlight(); 107 | } 108 | }); 109 | } 110 | }); 111 | 112 | // This callback is invoked when a new controller is attached: 113 | gamepadManager.onGamepadConnectedObservable.add((gamepad, state) => { 114 | // When a new controller is connected add support for detecting button presses 115 | gamepad.onButtonDownObservable.add((button, state) => { 116 | this.onPlayerFlight(); 117 | }); 118 | }); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /game/src/mainmenu.js: -------------------------------------------------------------------------------- 1 | var gamepadManager = new BABYLON.GamepadManager(); 2 | 3 | var deviceSourceManager; 4 | 5 | class MainMenu extends GameObject { 6 | constructor() { 7 | super(); 8 | } 9 | 10 | init() { 11 | this.visible = true; 12 | this._isVisible = false; 13 | this.createMainMenu(); 14 | this.setupInputs(); 15 | } 16 | 17 | onDestroy() {} 18 | 19 | update(deltaTime) { 20 | // Don't do anything here 21 | if (this._isVisible !== this.visible) { 22 | this._isVisible = this.visible; 23 | if (this._isVisible) { 24 | this.showUI(); 25 | } else { 26 | this.hideUI(); 27 | } 28 | } 29 | } 30 | 31 | onStartGame() { 32 | if (this.visible) { 33 | this.visible = false; 34 | createObject(new Player()); 35 | } 36 | } 37 | 38 | setupInputs() { 39 | deviceSourceManager = new BABYLON.DeviceSourceManager(scene.getEngine()); 40 | /** 41 | * onDeviceConnectedObservable is fired after a device is connected so any code that we 42 | * put in here should be able to reliably work against an existing device. 43 | * 44 | * For onInputChangedObservable, this will only work with Mouse, Touch, and Keyboards because 45 | * the Gamepad API currently does not fire input changed events (polling only) 46 | */ 47 | deviceSourceManager.onDeviceConnectedObservable.add((deviceSource) => { 48 | // If Mouse/Touch, add an Observer to change text 49 | if ( 50 | deviceSource.deviceType === BABYLON.DeviceType.Mouse || 51 | deviceSource.deviceType === BABYLON.DeviceType.Touch 52 | ) { 53 | deviceSource.onInputChangedObservable.add((eventData) => { 54 | if (eventData.type === "pointerdown" && eventData.inputIndex === BABYLON.PointerInput.LeftClick) { 55 | this.onStartGame(); 56 | } 57 | }); 58 | } 59 | // If Keyboard, add an Observer to change text 60 | else if (deviceSource.deviceType === BABYLON.DeviceType.Keyboard) { 61 | deviceSource.onInputChangedObservable.add((eventData) => { 62 | if (eventData.type === "keydown" && eventData.key === " ") { 63 | this.onStartGame(); 64 | } 65 | }); 66 | } 67 | }); 68 | 69 | // This callback is invoked when a new controller is attached: 70 | gamepadManager.onGamepadConnectedObservable.add((gamepad, state) => { 71 | // When a new controller is connected add support for detecting button presses 72 | gamepad.onButtonDownObservable.add((button, state) => { 73 | this.onStartGame(); 74 | }); 75 | }); 76 | } 77 | 78 | createMainMenu() { 79 | this.hudTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("Menu"); 80 | } 81 | 82 | getGreeting() { 83 | // Pick a random greeting from the list in constants.js 84 | let val = Math.floor(Math.random() * greetings.length); 85 | return greetings[val]; 86 | } 87 | 88 | showUI() { 89 | let greeting = this.getGreeting(); 90 | 91 | // Create a Text Block that can display the current score 92 | this.welcomeText = new BABYLON.GUI.TextBlock(); 93 | this.welcomeText.text = "Flying Cube Game!"; 94 | this.welcomeText.fontFamily = "Impact"; 95 | this.welcomeText.color = "white"; 96 | this.welcomeText.fontSize = 72; 97 | this.welcomeText.verticalAlignment = BABYLON.GUI.TextBlock.VERTICAL_ALIGNMENT_TOP; 98 | this.welcomeText.horizontalAlignment = BABYLON.GUI.TextBlock.HORIZONTAL_ALIGNMENT_CENTER; 99 | this.welcomeText.width = 0.5; 100 | this.welcomeText.height = 0.6; 101 | 102 | this.greetingText = new BABYLON.GUI.TextBlock(); 103 | this.greetingText.text = greeting; 104 | this.greetingText.fontFamily = "Impact"; 105 | this.greetingText.color = "white"; 106 | this.greetingText.fontSize = 28; 107 | this.greetingText.verticalAlignment = BABYLON.GUI.TextBlock.VERTICAL_ALIGNMENT_TOP; 108 | this.greetingText.horizontalAlignment = BABYLON.GUI.TextBlock.HORIZONTAL_ALIGNMENT_CENTER; 109 | this.greetingText.width = 0.5; 110 | this.greetingText.height = 0.7; 111 | 112 | this.instructionsText = new BABYLON.GUI.TextBlock(); 113 | this.instructionsText.text = "press any key to play"; 114 | this.instructionsText.fontFamily = "Impact"; 115 | this.instructionsText.color = "#aafffa"; 116 | this.instructionsText.fontSize = 32; 117 | this.instructionsText.verticalAlignment = BABYLON.GUI.TextBlock.VERTICAL_ALIGNMENT_TOP; 118 | this.instructionsText.horizontalAlignment = BABYLON.GUI.TextBlock.HORIZONTAL_ALIGNMENT_CENTER; 119 | this.instructionsText.width = 0.5; 120 | this.instructionsText.height = 0.9; 121 | 122 | this.hudTexture.addControl(this.welcomeText); 123 | this.hudTexture.addControl(this.greetingText); 124 | this.hudTexture.addControl(this.instructionsText); 125 | } 126 | 127 | hideUI() { 128 | this.hudTexture.removeControl(this.welcomeText); 129 | this.hudTexture.removeControl(this.greetingText); 130 | this.hudTexture.removeControl(this.instructionsText); 131 | } 132 | } 133 | 134 | var mainMenu = new MainMenu(); 135 | createObject(mainMenu); 136 | mainMenu.visible = true; 137 | --------------------------------------------------------------------------------