diff --git a/pages/docs/xr/xr-input.md b/pages/docs/xr/xr-input.md index a1d6f1d..d9b72c6 100644 --- a/pages/docs/xr/xr-input.md +++ b/pages/docs/xr/xr-input.md @@ -69,5 +69,5 @@ for (ezXRHand::Enum hand : {ezXRHand::Left, ezXRHand::Right}) ## See Also * [Input](../input/input-overview.md) -* [XR Overview](xr-overview.md.md) +* [XR Overview](xr-overview.md) * [XR Components](xr-components.md) \ No newline at end of file diff --git a/pages/docs/xr/xr-overview.md b/pages/docs/xr/xr-overview.md index 0de922f..d02a465 100644 --- a/pages/docs/xr/xr-overview.md +++ b/pages/docs/xr/xr-overview.md @@ -2,7 +2,7 @@ > **NOTE:** > -> XR support is still in development. You will need to enable *Show in Development Features* in the [editor settings](..\editor\editor-settings.md) to use it. +> XR support is still in development. You will need to enable *Show in Development Features* in the [editor settings](../editor/editor-settings.md) to use it. *XR* stands for both *VR* (virtual reality) as well as *AR* (augmented reality) devices. Currently supported devices: * **VR**: Windows desktop VR devices that support DX11 and OpenXR. diff --git a/pages/docs/xr/xr-project-setup.md b/pages/docs/xr/xr-project-setup.md index ce03189..ae80c10 100644 --- a/pages/docs/xr/xr-project-setup.md +++ b/pages/docs/xr/xr-project-setup.md @@ -4,11 +4,11 @@ As an example, we will use the [Testing Chambers](../../samples/testing-chambers.md) project and its **Surfaces** scene to set up XR rendering for desktop VR use. Start by opening the project and scene. -Enable *Show in Development Features* in the [editor settings](..\editor\editor-settings.md) and restart it to see XR features in the editor. +Enable *Show in Development Features* in the [editor settings](../editor/editor-settings.md) and restart it to see XR features in the editor. To use XR in your project you must first load a plugin to support XR devices. In the [plugin selection](../projects/plugin-selection.md) dialog, select your custom XR plugin. In this case, we select the *Open XR* plugin and close the dialog. -Next is to enable XR rendering under [asset profiles (TODO)](asset-profiles.md). Select the profile you want to enable XR in and then check the *Enable XR* checkbox. You also need to select the *XR Render Pipeline* here. Currently, both the `MainRenderPipeline` and the `HololensRenderPipeline` fully support XR rendering. Let's select the `MainRenderPipeline` for this example and close the dialog. +Next is to enable XR rendering under [asset profiles (TODO)](../assets/asset-profiles.md). Select the profile you want to enable XR in and then check the *Enable XR* checkbox. You also need to select the *XR Render Pipeline* here. Currently, both the `MainRenderPipeline` and the `HololensRenderPipeline` fully support XR rendering. Let's select the `MainRenderPipeline` for this example and close the dialog. Pressing 'Play the Game' in the scene should now already start rendering on the HMD and you can look around but it will not be very interactive. diff --git a/pages/samples/monster-attack/devlog-1.md b/pages/samples/monster-attack/devlog-1.md new file mode 100644 index 0000000..ea10ae2 --- /dev/null +++ b/pages/samples/monster-attack/devlog-1.md @@ -0,0 +1,19 @@ +# MA Devlog 1 - Intro + +For some reason the next two weeks I have more spare time than usual. So I decided to use this time to try to make a demo that is a bit more elaborate. And I decided to mention this here, so that I have more reasons to actually continue working on it. You know, I'm more of a technical person, and doing "game development" quickly becomes boring to me. I prefer to write the systems behind the scenes. Anyway, this project is limited to two weeks anyway, so let's see how far I get. + +Since I'm bad at game design, I need something that's already designed. So I chose to do a tower defense game. More specifically, a clone of [Orcs Must Die!](https://en.wikipedia.org/wiki/Orcs_Must_Die!), a game that is a lot of fun (though I only know part 1 and 2). + +The nice thing about this game is, that the core mechanic is relatively quick to implement, but then there are lots and lots of details that can be added over time. + +In the past few days I've set up some basics of the project, gathered some assets (from and ) and implemented the basic character controller and monster functionality. So I now have a simple level layout and some monsters that run from the start point to the end point: + + + +You can also already shoot and kill the monsters: + + + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-2.md b/pages/samples/monster-attack/devlog-2.md new file mode 100644 index 0000000..4aa141f --- /dev/null +++ b/pages/samples/monster-attack/devlog-2.md @@ -0,0 +1,84 @@ +# MA Devlog 2 - Spike Trap + +So today was all about getting my first trap working. Here is the result: + + + +The most flexible way to do these things, is to just use [custom C++ components](../../docs/custom-code/cpp/custom-cpp-component.md) for everything. However, I want to test out our other infrastructure as well, for example the new [visual scripting](../../docs/custom-code/visual-script/visual-script-overview.md), the [state machines](../../docs/ai/state-machine-asset.md), and so on, so the goal is to prefer those, and only use C++ for the things that really need it. + +At the moment I only have two custom C++ components, one for the player logic and one for the monsters. The former mostly does [input handling](../../docs/input/input-overview.md) and forwarding to the [character controller](../../docs/physics/jolt/special/jolt-character-controller.md), the latter mainly does the path finding and steering. + +So today I had to figure out how to build my first trap. + +If I were (or had) an artist, I would use an [animated mesh](../../docs/animation/skeletal-animation/animated-mesh-asset.md) with multiple [animations](../../docs/animation/skeletal-animation/animation-clip-asset.md) for the different trap states. However, I only have two [static meshes](../../docs/graphics/meshes/mesh-asset.md), one with spikes, one without, and I really want to make things work with the assets as I have them. So I needed a way to do the visuals without animations. + +Here is what it looks like in a close up: + + + +The trap has four states, therefore I built a [state machine](../../docs/ai/state-machine-asset.md). + +![Spike Trap States](media/devlog2/ma-dl2-image1.png) + +In the *Dormant* state it does nothing. This is the state where the trap is not dangerous. It takes a second and then transitions into the *Loaded* state. + +In the loaded state, the spikes show up and peak through the bottom. Now when a creature walks over it, the trap enters the *Firing* state where it makes damage. + +After a short time, it enters the *Retracting* state, where the spikes move down and then it starts over. + +Again, there are different ways how you could achieve the animations and the behavior, but I wanted to use as much existing functionality as possible. For each *State* in the state machine you select what code it should run. And one existing type of state is the **Switch Object** state. What this will do, is it simply activates / deactivates game objects in your object structure. So you could for example use this to enable a particle effect node and thus make your object *burning* or switch to a different mesh object, so that it looks broken. + +For example the *Retracting* state deactivates all objects under the *States* group, but enables the *Retracting* object: + +![Retracting State](media/devlog2/ma-dl2-image2.png) + +My trap prefab looks like this: + +![Trap Layout](media/devlog2/ma-dl2-image3.png) + +On the root node, there is my state machine component. + +Directly attached to it are the base mesh (second to last node) and a trigger (last node). Inside the *States* group there are the four different groups that represent the different states. For example, the *Dormat* group is just empty. + +The three other groups add the spiky mesh and the *Firing* group additionally adds area damage. + +Now as I said, for the animation one should use a skinned mesh and just play animations, but to achieve the up/down movement without this, I used the [Slider component](../../docs/animation/property-animation/slider-component.md). + +Also I added a new *Reset Transform* component, to make the sub-objects move back into place each time. So now the *Retracting* and *Firing* group uses these components: + +![Components](media/devlog2/ma-dl2-image4.png) + +All this gives you the visuals. + +Now on the state machine you have *transitions* (the arrows between the states). For each transition you again have to choose the "type". The transition type determines the logic to decide whether the transition should be taken or not. So you can have complex logic here. + +To start, I just used the **Timeout** transition, which would just cycle through the states with a 1 second delay. +This is fine for all transitions, except for the one from *Loaded* to *Firing*. Here we only want to transition when a creature walks into the trap. + +To detect this, I decided to use a [physics trigger](../../docs/physics/jolt/actors/jolt-trigger-component.md). + +First this meant that my creatures need to have some kind of physics representation, which the physics trigger can detect at all. So I added a kinematic capsule shape, which just moves along with my creature (they currently don't use a character controller). + +![Creature](media/devlog2/ma-dl2-image5.png) + +Now whenever the creature walks into a trap, the trigger in my trap prefab will fire an event. However, so far this event won't have any effect. + +I need to hook up the event from the trigger to my state machine. And this is what [visual scripts](../../docs/custom-code/visual-script/visual-script-overview.md) are really good for. + +On the root node of my trap, I added a *Script Component*: + +![Script component](media/devlog2/ma-dl2-image6.png) + +The script is quite trivial: + +![Script](media/devlog2/ma-dl2-image7.png) + +Whenever the trigger fires, the script's *OnMsgTriggerTriggered* node gets executed. We then switch over the state and only react to *Activated* events. If so, we call *FireTransitionEvent* on the sibling [StateMachineComponent](../../docs/ai/state-machine-component.md) and tell it to *Fire*. + +![SM transitions](media/devlog2/ma-dl2-image8.png) + +So now we have it. When a creature walks into the trigger, the visual script gets the physics trigger event, forwards that to the state machine and when the state machine happens to be in the *Loaded* state, it will transition into the *Firing* state. The state machine then changes which sub-objects in the trap are active, which in turn starts the spike movement and applies area damage to anything close by. + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-3.md b/pages/samples/monster-attack/devlog-3.md new file mode 100644 index 0000000..7074a98 --- /dev/null +++ b/pages/samples/monster-attack/devlog-3.md @@ -0,0 +1,72 @@ +# MA Devlog 3 - Sound + +Today I spent some time on improving and polishing what I built so far. + +First I set up sound. EZ uses Fmod, so [following the documentation](../../docs/sound/fmod-overview.md) and my own tutorial video, I created a new Fmod Studio project, downloaded a couple of sounds from and added sounds for footsteps, the player's projectile and the trap. + +Footsteps for the player are quite easy, because the [character controller](../../docs/physics/jolt/special/jolt-character-controller.md) already supports this through [surface interactions](../../docs/materials/surfaces.md). Basically, whenever something needs to interact with a surface, for example a bullet hitting a wall, we can easily spawn a *surface interaction*. Usually polygons are linked to a surface and the surface acts as a lookup table. So if I walk over a stone surface and I want to spawn a *footstep* interaction, the stone surface defines which prefab to use, and if I walk over a metal surface, it may define a different prefab to use. + +![Footstep config](media/devlog3/ma-dl3-image1.png) + +Surfaces are hierarchical, so both stone and metal surfaces are built on top of the *Default* surface, and as long as they don't define an override, the value from the *Default* surface is used. + +Therefore, on the character controller, all that we need to define is the name of the surface interaction, and how quickly those should be spawned when walking or running. + +![Footstep config](media/devlog3/ma-dl3-image2.png) + +The same system is used by the [projectile](../../docs/gameplay/projectile-component.md) to spawn a prefab when it hits something. + +For my trap, all I needed to add was a sound event to the *Loaded* state, so that it makes a mechanical noise when it is ready. + +![Trap Sound](media/devlog3/ma-dl3-image3.png) + +The next thing I did, was to add code to the player component, so that the player can place traps. This is all done in C++. + +EZ has an [abstract interface](../../docs/runtime/configuration/interfaces.md) `ezPhysicsWorldModuleInterface` which you can query from the [world](../../docs/runtime/world/world-overview.md), that gives you access to things like raycasts. To get this interface, you call + +`ezPhysicsWorldModuleInterface* pPhysics = GetOwner()->GetWorld()->GetModule();` + +This is probably one of the most important such interfaces, since physics queries are so ubiquitously useful for all sorts of game mechanics. + +For now I simply use this to check where the player is looking. Meaning, I shoot a ray through the camera like this: + +```cpp +ezPhysicsCastResult result; +pPhysics->Raycast(result, pCameraObject->GetGlobalPosition(), pCameraObject->GetGlobalDirForwards(), 10.0f, params); +``` + +Now I have the point that the player is looking at. The next step is to validate, that one can place a trap there. For now I only do very simply position snapping and some rotation, I don't yet prevent the player from placing traps where they don't belong. + +Of course, while in trap placement mode you want to have a preview how things would look like, so I built a copy of my trap prefab, that has no functionality, and I add that to the scene (and move it around) to show where the trap would end up. At some point this should probably also use a [custom shader (TODO)](../../docs/graphics/shaders/shaders-overview.md) for a nice "preview effect", and some sounds when placing traps for better feedback. + + + +At some point I noticed that when a monster dies, it doesn't dissappear and a lot of bodies where piling up. So now the }*monster component* simply puts itself into a queue when it starts playing the *die* animation to keep track of dead bodies, and I delete the oldest one when I have more than 20. + +Finally, I wanted to have a second trap. My favourite trap in *Orcs Must Die* is the one that shoots arrows out of a wall. From its logic it's very similar to the spike trap, so I copied and adjusted it. This is what it looks like: + +![Arrow Trap](media/devlog3/ma-dl3-image4.png) + +I've added a *Dart* [projectile](../../docs/gameplay/projectile-component.md) prefab and this trap simply uses 24 [spawn components](../../docs/gameplay/spawn-component.md) to shoot a lot of those. This is it in action: + + + +However, I wanted the trap to shoot arrows three times in quick succession. Because that's way cooler. Turns out, this was absolutely trivial to do with the state machine. All I needed to do, was to copy two of the states a few times and set up a short time delay as transitions: + +![Arrow Trap](media/devlog3/ma-dl3-image5.png) + +And now it behaves like this: + + + +And together they already create quite some mayhem: + + + +Finally, in game, it looks and sounds like this: + + + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-4.md b/pages/samples/monster-attack/devlog-4.md new file mode 100644 index 0000000..736655a --- /dev/null +++ b/pages/samples/monster-attack/devlog-4.md @@ -0,0 +1,47 @@ +# MA Devlog 4 - Tar Trap + +Today I added a simple trap that just slows down monsters when they walk over it. + + + +As always, there are many ways how one can implement this. I chose to simply do a raycast at the monster position downwards and find the physics object beneath their feet. From there I could use the [surface](../../docs/materials/surfaces.md) to determine whether to slow down the monster. + +That means that my trap needs to actually contain a [physical collider](../../docs/physics/jolt/collision-shapes/jolt-collision-meshes.md) object, so that the raycast can hit anything. Therefore my trap now looks like this: + +![Tar Trap](media/devlog4/TarTrap.png) + +Here I use a [query shape actor](../../docs/physics/jolt/actors/jolt-queryshape-actor-component.md) rather than a static actor, because I only want raycasts to be able to hit this collider, I do not want any other objects (like my player's character controller) to collide with it. + +But that also means that when I do my raycast, I need to make sure to include *Query objects*: + +![Query Objects Flag](media/devlog4/ma-dl4-image1.png) + +Now this kinda worked, but I ran into the problem that the raycast would often not hit the ground, but the monster itself. + +To show the problem, in the video above I enabled the skeleton collider visualization for the blue monsters. Those are the animated shapes that are used for shooting the monsters. My raycast would sometimes hit that and then not detect the correct ground type. + +I had to set up proper [collision layers](../../docs/physics/jolt/collision-shapes/jolt-collision-layers.md) and assign the right ones to the monster shapes, the ground shapes, and the raycast. + +Unfortunately this isn't fun, because there is a limit of 32 layers and you have to be very careful how to set them up, because their number can quickly grow. + +For now my setup looks like this: + +![Query Objects Flag](media/devlog4/CollisionGroups.png) + +For debugging purposes, I wanted to see when exactly the monsters are slowed down, so I used the `ezMsgSetColor` to just dim the monsters mesh color when slowed: + +![Set Color Msg](media/devlog4/ma-dl4-image2.png) + +Finally, I exposed the health and walk speed as properties, so that I could configure my two monster types slightly differently. + +In C++ this macro block is used to declare which variables are configurable: + +![Component properties](media/devlog4/ma-dl4-image3.png) + +And then these show up in the editor: + +![Property UI](media/devlog4/ma-dl4-image4.png) + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-5.md b/pages/samples/monster-attack/devlog-5.md new file mode 100644 index 0000000..5c2bc0d --- /dev/null +++ b/pages/samples/monster-attack/devlog-5.md @@ -0,0 +1,19 @@ +# MA Devlog 5 - Level Logic + +Today I've looked into doing some of the level logic. Meaning when monsters are spawned, when you win or lose, and so on. I've also added that placing traps costs money and you only have a limited amount of money. + +It's all working, but just so, and I'm not happy enough with it, that I think it already makes sense to dive deep into any of it. However, I can post some teasers at least. Here is the state machine for my test level: + +![State Machine](media/devlog5/ma-dl5-image1.png) + +Since the game is very linear in nature, working with state machines makes a lot of sense, and makes my life indeed much easier. I can create building blocks for the different phases, and if I ever build multiple levels, they can be easily re-used in different configurations. + +For example here is the visual script that is used by the *Countdown* phase: + +![State Script](media/devlog5/ma-dl5-image2.png) + +It's using the cool new *coroutine* feature to run a script across many frames. It literally just counts down a number from 3 to 1 and then ends. In parallel the *Update* hook makes sure to print that number to screen every frame. Quite neat. + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-6.md b/pages/samples/monster-attack/devlog-6.md new file mode 100644 index 0000000..2587bda --- /dev/null +++ b/pages/samples/monster-attack/devlog-6.md @@ -0,0 +1,61 @@ +# MA Devlog 6 - More Level Logic + +The last days I ran into expected problems. After having a prototype up an running, I found several bugs and encountered usability issues. + +My main usability issue was with the global state handling. The game needs to track a few things (for example, how many monsters are currently alive). These values need to be initialized and reset properly every time I start a level. I do all development from inside the editor, so I restart the game frequently, and this needs to work reliably. + +So one thing I did to make this easier, is to add the [Global Blackboard Component](https://github.com/ezEngine/ezEngine/pull/1065). All it does, is to initialize blackboard values for you at level start. Then all other scripts work as desired. Since global state is truly global, it would otherwise persist even across runs and across level transitions (which can be useful). + +Another thing I needed, was to spawn many monsters in an area over a certain time, so that they disperse a bit. I've previously hacked this together using the [spawn component](../../docs/gameplay/spawn-component.md) but since this is a quite useful feature, I decided to write a proper [new component](https://github.com/ezEngine/ezEngine/pull/1070) for this. + +So now I can do this: + + + +Of note here is that all our "gameplay" components are mostly meant as demonstrations how one could do some feature. If those components are sufficient for you, that's great, but for a lot of use cases they won't be perfect and you are encouraged to write your own. The great thing about open source is obviously, that you can look at how the built-in features are done for inspiration. So these components give you a decent starting point for doing copy & paste, or how I like to call it "CTRL+C, CTRL+Inspire" ;) + +Another thing I've done, is to get my level state machine working properly. It currently looks like this: + +![LevelStateMachine](media/devlog6/ma-dl6-LevelStateMachine.png) + +This nicely visualizes the level logic. We start at the initial state, after that the player has unlimited time to build traps (but limited money). The game displays "Press 'G' to start". Once the player does so, a short countdown is displayed, then the first wave of monsters is spawned and the player has to fight. Once all monsters from that wave are dead, a second countdown is shown, the second wave of (different) enemies is spawned and finally when all are dead, the player wins. + +State machines are made up of states and transitions between them. Only one state is active at any time. When a state is active, it may execute code. This can be C++ or *Visual Script* code. + +For example the "Wave 1" state looks like this: + +![SMStateProperty](media/devlog6/ma-dl6-SMStateProperty.png) + +Here I've selected the *script* state type and given it my custom script. Additionally I can pass in parameters, so here I tell it which game object to use for spawning monsters. This way I can reuse the same script for different monster types. + +The script isn't very spectacular: + +![VSWave](media/devlog6/ma-dl6-VSWave.png) + +All it does is find the desired game object via a [global key](../../docs/runtime/world/game-objects.md#global-keys), retrieves its spawn box component and tells it to start spawning. + +The actual game logic is mostly implemented on the transitions. Every state may have multiple outgoing transitions to different states. If the condition of a transition is met, the state machine follows that transition and switches to another state. What transition types are available can be extended with C++, but the most common ones are the *timeout transition*, which just waits for a bit, and the *blackboard transition* which checks whether some values are as desired. + +For example here is the *Fight -> Countdown* transition setup: + +![SMTransitionProperty](media/devlog6/ma-dl6-SMTransitionProperty.png) + +This uses a compound to combine multiple transition types. Here a timeout of 10 seconds is used to enforce a minimum delay. Additionally we wait for the `Monsters` value in the blackboard to reach the value 0. Once this is the case, the *Fight* phase is over and the next phase can begin. + +Finally, you certainly noticed the *Any State*. This is a convenience feature, that allows you to basically add a transition from all states to another state, without having to set all that up manually. The *Any State* itself doesn't do anything, it is a placeholder. What you do configure, is the transition: + +![SMAnyStateTransition](media/devlog6/ma-dl6-SMAnyStateTransition.png) + +Here our simple logic just says, that if the `Points` blackboard value reaches zero, we transition into the *Lost* state. You lose points every time a monster arrives at the level end. + +So how do we get all this running in our game? We just add a [State Machine](../../docs/ai/state-machine-component.md) object into our scene: + +![LevelNodes](media/devlog6/ma-dl6-LevelNodes.png) + +Here I also gave the object the global key **LevelLogic**, so that my C++ custom [game state](../../docs/runtime/application/game-state.md) can also find it and send a message to it for the `G` input, but other than that it is self sufficient. + +Well, that was a long post. If you managed to read this far, here is a [video of it all in action](https://1drv.ms/v/s!Ajrhg3sdAbZvltMpVgWHMQ76muk9xw?e=NNngvl). + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/devlog-7.md b/pages/samples/monster-attack/devlog-7.md new file mode 100644 index 0000000..12eb5a0 --- /dev/null +++ b/pages/samples/monster-attack/devlog-7.md @@ -0,0 +1,53 @@ +# MA Devlog 7 - Level Pieces + +Yesterday I spent some time turning my greybox level into something actually presentable. + +For this I used the great assets from [Quaternius](https://quaternius.com). + +Here are a few screenshots of the results: + +![Screenshot 1](media/devlog7/ma-dl7-Screenshot1.jpg) + +![Screenshot 2](media/devlog7/ma-dl7-Screenshot2.jpg) + +![Screenshot 3](media/devlog7/ma-dl7-Screenshot3.jpg) + +![Screenshot 4](media/devlog7/ma-dl7-Screenshot4.jpg) + +Since my gameplay is modular (traps can only be placed on a grid), my level design also needs to cater to this, and my workflow can benefit from it. + +My first attempt was to place the modular assets as I thought they should fit. This didn't work out, though, and I needed to go for a more rigid approach. So my next attempt was to build modular prefabs that always take up either 2x2 meters or a multiple of that. + +So a floor piece looks like this: + +![Floor4x4](media/devlog7/ma-dl7-Floor4x4.jpg) + +Now I needed some guidance on how to place walls. Would you place walls ON the floor plates? That's what I did in my first, failed attempt. So this time I made wall pieces take up their own tile and to make this very clear during placement, they contain their floor section as well: + +![Wall](media/devlog7/ma-dl7-Wall.jpg) + +Thus my level is made up of relatively large and easy to place pieces: + +![WallPiece](media/devlog7/ma-dl7-WallPiece.jpg) + +![FloorPiece](media/devlog7/ma-dl7-FloorPiece.jpg) + +Once I had my level rebuilt with these pieces, I wanted some lighting and decoration. This becomes annoying pretty fast, especially when you want certain things to always look the same. For example the torches were all supposed to be in similar places (same height etc). + +So I had the idea to just put some decoration elements directly into my wall prefabs and make them an optional switch. I used [exposed prefab parameters](../../docs/scenes/exposed-parameters.md) to achieve this. Basically, I just expose the *Active* property of some group object under a nice name. + +For example for the floor prefab I added that it can optionally have a ceiling, and I added a second option to have a ceiling with a grate in it: + +![CeilingBars](media/devlog7/ma-dl7-CeilingBars.jpg) + +So now when I place this prefab, I get these options: + +![ExposedParams](media/devlog7/ma-dl7-ExposedParams.png) + +With these prefabs at hand, building my level was quite straight forward and it is relatively quickly modified. + +It's always amazing, how a few nice assets turn something from "crappy proof of concept" into "looks like a proper game", even though it's still way closer to the former :D + +## See Also + +* [Monster Attack Sample](monster-attack.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/media/MonsterAttack-Main.jpg b/pages/samples/monster-attack/media/MonsterAttack-Main.jpg new file mode 100644 index 0000000..3ed6cdb Binary files /dev/null and b/pages/samples/monster-attack/media/MonsterAttack-Main.jpg differ diff --git a/pages/samples/monster-attack/media/devlog1/Navigation.mp4 b/pages/samples/monster-attack/media/devlog1/Navigation.mp4 new file mode 100644 index 0000000..1a6d8b2 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog1/Navigation.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog1/Shoot.mp4 b/pages/samples/monster-attack/media/devlog1/Shoot.mp4 new file mode 100644 index 0000000..2b47ae1 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog1/Shoot.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-SpikeTrapAnim.mp4 b/pages/samples/monster-attack/media/devlog2/ma-dl2-SpikeTrapAnim.mp4 new file mode 100644 index 0000000..c793eae Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-SpikeTrapAnim.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-Spike_Trap.mp4 b/pages/samples/monster-attack/media/devlog2/ma-dl2-Spike_Trap.mp4 new file mode 100644 index 0000000..b64bf6c Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-Spike_Trap.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image1.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image1.png new file mode 100644 index 0000000..8cdd1a0 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image1.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image2.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image2.png new file mode 100644 index 0000000..691383d Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image2.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image3.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image3.png new file mode 100644 index 0000000..dfcff6c Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image3.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image4.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image4.png new file mode 100644 index 0000000..4dfc02c Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image4.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image5.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image5.png new file mode 100644 index 0000000..6e270fc Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image5.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image6.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image6.png new file mode 100644 index 0000000..e10ff82 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image6.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image7.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image7.png new file mode 100644 index 0000000..cc3071b Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image7.png differ diff --git a/pages/samples/monster-attack/media/devlog2/ma-dl2-image8.png b/pages/samples/monster-attack/media/devlog2/ma-dl2-image8.png new file mode 100644 index 0000000..3470a4d Binary files /dev/null and b/pages/samples/monster-attack/media/devlog2/ma-dl2-image8.png differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap.mp4 b/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap.mp4 new file mode 100644 index 0000000..69adc2e Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap2.mp4 b/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap2.mp4 new file mode 100644 index 0000000..6a2d034 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-ArrowTrap2.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay.mp4 b/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay.mp4 new file mode 100644 index 0000000..e30f429 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay2.mp4 b/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay2.mp4 new file mode 100644 index 0000000..ef8acc0 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-Gameplay2.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-TrapPlacement.mp4 b/pages/samples/monster-attack/media/devlog3/ma-dl3-TrapPlacement.mp4 new file mode 100644 index 0000000..3375bf3 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-TrapPlacement.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-image1.png b/pages/samples/monster-attack/media/devlog3/ma-dl3-image1.png new file mode 100644 index 0000000..b66c1eb Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-image1.png differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-image2.png b/pages/samples/monster-attack/media/devlog3/ma-dl3-image2.png new file mode 100644 index 0000000..bef3839 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-image2.png differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-image3.png b/pages/samples/monster-attack/media/devlog3/ma-dl3-image3.png new file mode 100644 index 0000000..5ca937a Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-image3.png differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-image4.png b/pages/samples/monster-attack/media/devlog3/ma-dl3-image4.png new file mode 100644 index 0000000..87c8398 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-image4.png differ diff --git a/pages/samples/monster-attack/media/devlog3/ma-dl3-image5.png b/pages/samples/monster-attack/media/devlog3/ma-dl3-image5.png new file mode 100644 index 0000000..6743be6 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog3/ma-dl3-image5.png differ diff --git a/pages/samples/monster-attack/media/devlog4/CollisionGroups.png b/pages/samples/monster-attack/media/devlog4/CollisionGroups.png new file mode 100644 index 0000000..755420e Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/CollisionGroups.png differ diff --git a/pages/samples/monster-attack/media/devlog4/SlowDown.mp4 b/pages/samples/monster-attack/media/devlog4/SlowDown.mp4 new file mode 100644 index 0000000..61669d0 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/SlowDown.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog4/TarTrap.png b/pages/samples/monster-attack/media/devlog4/TarTrap.png new file mode 100644 index 0000000..3594e2a Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/TarTrap.png differ diff --git a/pages/samples/monster-attack/media/devlog4/ma-dl4-image1.png b/pages/samples/monster-attack/media/devlog4/ma-dl4-image1.png new file mode 100644 index 0000000..44e3aa1 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/ma-dl4-image1.png differ diff --git a/pages/samples/monster-attack/media/devlog4/ma-dl4-image2.png b/pages/samples/monster-attack/media/devlog4/ma-dl4-image2.png new file mode 100644 index 0000000..e10ad7e Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/ma-dl4-image2.png differ diff --git a/pages/samples/monster-attack/media/devlog4/ma-dl4-image3.png b/pages/samples/monster-attack/media/devlog4/ma-dl4-image3.png new file mode 100644 index 0000000..b5d41cf Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/ma-dl4-image3.png differ diff --git a/pages/samples/monster-attack/media/devlog4/ma-dl4-image4.png b/pages/samples/monster-attack/media/devlog4/ma-dl4-image4.png new file mode 100644 index 0000000..7fd4bbb Binary files /dev/null and b/pages/samples/monster-attack/media/devlog4/ma-dl4-image4.png differ diff --git a/pages/samples/monster-attack/media/devlog5/ma-dl5-image1.png b/pages/samples/monster-attack/media/devlog5/ma-dl5-image1.png new file mode 100644 index 0000000..dde050f Binary files /dev/null and b/pages/samples/monster-attack/media/devlog5/ma-dl5-image1.png differ diff --git a/pages/samples/monster-attack/media/devlog5/ma-dl5-image2.png b/pages/samples/monster-attack/media/devlog5/ma-dl5-image2.png new file mode 100644 index 0000000..2618dfd Binary files /dev/null and b/pages/samples/monster-attack/media/devlog5/ma-dl5-image2.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelNodes.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelNodes.png new file mode 100644 index 0000000..ab7ce8e Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelNodes.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelStateMachine.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelStateMachine.png new file mode 100644 index 0000000..efee89c Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-LevelStateMachine.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-SMAnyStateTransition.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMAnyStateTransition.png new file mode 100644 index 0000000..3e57ccc Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMAnyStateTransition.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-SMStateProperty.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMStateProperty.png new file mode 100644 index 0000000..d5a8d8c Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMStateProperty.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-SMTransitionProperty.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMTransitionProperty.png new file mode 100644 index 0000000..662669a Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-SMTransitionProperty.png differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-SpawnBox.mp4 b/pages/samples/monster-attack/media/devlog6/ma-dl6-SpawnBox.mp4 new file mode 100644 index 0000000..d358654 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-SpawnBox.mp4 differ diff --git a/pages/samples/monster-attack/media/devlog6/ma-dl6-VSWave.png b/pages/samples/monster-attack/media/devlog6/ma-dl6-VSWave.png new file mode 100644 index 0000000..9588970 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog6/ma-dl6-VSWave.png differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-CeilingBars.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-CeilingBars.jpg new file mode 100644 index 0000000..a914d69 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-CeilingBars.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-ExposedParams.png b/pages/samples/monster-attack/media/devlog7/ma-dl7-ExposedParams.png new file mode 100644 index 0000000..91b44d1 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-ExposedParams.png differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Floor4x4.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Floor4x4.jpg new file mode 100644 index 0000000..f16c0fb Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Floor4x4.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-FloorPiece.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-FloorPiece.jpg new file mode 100644 index 0000000..192d561 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-FloorPiece.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot1.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot1.jpg new file mode 100644 index 0000000..f69eff6 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot1.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot2.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot2.jpg new file mode 100644 index 0000000..6d19847 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot2.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot3.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot3.jpg new file mode 100644 index 0000000..5474b55 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot3.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot4.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot4.jpg new file mode 100644 index 0000000..81b25a0 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Screenshot4.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-Wall.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-Wall.jpg new file mode 100644 index 0000000..58e0e66 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-Wall.jpg differ diff --git a/pages/samples/monster-attack/media/devlog7/ma-dl7-WallPiece.jpg b/pages/samples/monster-attack/media/devlog7/ma-dl7-WallPiece.jpg new file mode 100644 index 0000000..9b4f528 Binary files /dev/null and b/pages/samples/monster-attack/media/devlog7/ma-dl7-WallPiece.jpg differ diff --git a/pages/samples/monster-attack/monster-attack.md b/pages/samples/monster-attack/monster-attack.md new file mode 100644 index 0000000..76f4ff2 --- /dev/null +++ b/pages/samples/monster-attack/monster-attack.md @@ -0,0 +1,26 @@ +# Monster Attack Sample + +*Monster Attack* (**MA**) is a tower-defense sample game inspired by the game [Orcs Must Die!](https://en.wikipedia.org/wiki/Orcs_Must_Die!). + +![Monster Attack](media/MonsterAttack-Main.jpg) + +The sample demonstrates the use of a wide range of features. Parts of its development are covered in a series of **Devlogs**. + +The code and data are hosted in a separate GitHub repository. + +Please see its main [Readme file](https://github.com/ezEngine/project-monster-attack) for up-to-date build instructions, game controls and other details. + +## Video + +Click the image below for a demo playthrough video: + +[![Demo Playthrough](https://img.youtube.com/vi/upe5Hbk8z-w/0.jpg)](https://www.youtube.com/watch?v=upe5Hbk8z-w) + +## Devlogs + +This sample is accompanied by a series of *Devlogs* (see sidebar on the left) which describe how to use various engine features to accomplish different game mechanics. + +## See Also + +* [Samples](../samples-overview.md) +* [Videos](../../getting-started/videos.md) \ No newline at end of file diff --git a/pages/samples/monster-attack/toc.txt b/pages/samples/monster-attack/toc.txt new file mode 100644 index 0000000..2b2bd11 --- /dev/null +++ b/pages/samples/monster-attack/toc.txt @@ -0,0 +1,8 @@ +monster-attack.md +devlog-1.md +devlog-2.md +devlog-3.md +devlog-4.md +devlog-5.md +devlog-6.md +devlog-7.md \ No newline at end of file diff --git a/pages/samples/toc.md b/pages/samples/toc.md index bdfd1dc..e451331 100644 --- a/pages/samples/toc.md +++ b/pages/samples/toc.md @@ -3,6 +3,15 @@ # [Asteroids Sample](asteroids.md) # [Compute Shader Histogram Sample](cs-histogram.md) # [LineCount Sample](line-count.md) +# Monster Attack +## [Monster Attack Sample](monster-attack/monster-attack.md) +## [MA Devlog 1 - Intro](monster-attack/devlog-1.md) +## [MA Devlog 2 - Spike Trap](monster-attack/devlog-2.md) +## [MA Devlog 3 - Sound](monster-attack/devlog-3.md) +## [MA Devlog 4 - Tar Trap](monster-attack/devlog-4.md) +## [MA Devlog 5 - Level Logic](monster-attack/devlog-5.md) +## [MA Devlog 6 - More Level Logic](monster-attack/devlog-6.md) +## [MA Devlog 7 - Level Pieces](monster-attack/devlog-7.md) # [PacMan Sample](pacman.md) # [RTS Sample](rts.md) # [Sample Game Plugin](sample-game-plugin.md) diff --git a/pages/samples/toc.txt b/pages/samples/toc.txt index cb0018c..ae73300 100644 --- a/pages/samples/toc.txt +++ b/pages/samples/toc.txt @@ -3,6 +3,7 @@ screenshots.md asteroids.md cs-histogram.md line-count.md +monster-attack pacman.md rts.md sample-game-plugin.md diff --git a/scripts/generate_toc.bat b/scripts/generate_toc.bat index 877ec1e..f9edc37 100644 --- a/scripts/generate_toc.bat +++ b/scripts/generate_toc.bat @@ -3,6 +3,6 @@ REM set current working directory to this folder's parent, because that's what the create-toc script expects Pushd %cd%\.. echo Generating TOC... -py scripts\create-toc.py +python scripts\create-toc.py popd