tag:blogger.com,1999:blog-64217653356593810702024-02-19T07:20:44.635+00:00TrederiaReflecting upon my adventures in codeMatt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.comBlogger79125tag:blogger.com,1999:blog-6421765335659381070.post-20554720245437926312019-10-11T19:00:00.000+01:002019-10-11T19:00:02.341+01:00Getting started with xygine - Part 6<h3>
Pop & Sparkle - That Magic Touch</h3>
In the final part of the tutorial let's look at xygine's <code>Director</code> class, and see how
it can be used to add some of the finer details to the game with the <code>AudioEmitter</code> and
<code>ParticleEmitter</code> components.<br />
<h3>
The Director</h3>
The <code>Director</code> class in xygine differs slightly from the other ECS based classes, in
that while it is a member of the <code>Scene</code>, it doesn't interact directly with it.
The <code>Director</code> class instead monitors the <code>Scene</code> via the event queue and
<code>MessageBus</code>, and dispatches commands or creates new entities based on what it has
observed - hence the title <code>Director</code>.<br />
The class itself is actually an abstract one - to use it we'll need to create our own
class which inherits it. In this case we will use the <code>Director</code> to observe <code>Block</code>s
being destroyed by using the <code>Director</code>'s message handler, and dispatch entities with
an <code>AudioEmitter</code> and <code>ParticleEmitter</code> components attached to create a special effect.
This class we'll call the <code>FXDirector</code>.<br />
<h3>
Set up</h3>
Before diving into creating the new class I'll briefly cover two new components we'll be
using.<br />
<br />
First is the <code>AudioEmitter</code>. <code>AudioEmitter</code> components require an audio resource
to play, similarly to <code>Text</code> needing a font or <code>Sprite</code> requiring a <code>Texture</code>.
<code>AudioEmitter</code>s can play two different types of resources, however. Short sounds can be
buffered in memory or stored in an <code>sf::SoundBuffer</code>. <code>sf::SoundBuffer</code>s will happily be
managed by the <code>xy::ResourceHandler</code> so that they can be shared between multiple
<code>AudioEmitter</code> components. Before an <code>AudioEmitter</code> can play a sound it needs to have
the source of the audio set with <code>AudioEmitter::setSource()</code>. This takes a reference to
an <code>sf::SoundBuffer</code>, but it can also be passed a string. This string should contain a
path to a resource on disk, in which case the the <code>AudioEmitter</code> will attempt to stream
the sound directly from the file. This is useful for larger audio files such as music,
and supports compressed formats like .ogg. When using an <code>AudioEmitter</code> it's worth
considering what kind of audio it will be playing before you decide what to pass to
<code>setSource()</code>.<br />
<br />
To use the <code>AudioEmitter</code> in a <code>Scene</code> we'll need to add a <code>xy::AudioSystem</code>. The
<code>AudioSystem</code> takes care of positional audio for any mono sounds, as well as making sure
that the volume levels are correctly updated by the mixer displayed in the Audio tab,
when opening the console with F1. It only ever needs to be added to a single active
<code>Scene</code> as it will, by its nature, affect all audio output. In this case we'll add it to
<code>m_gameScene</code>.<br />
<br />
The <code>ParticleEmitter</code> component, as its name suggests, emits particles. The
configuration of the emitter can be controlled via the <code>xy::EmitterSettings</code> struct
which has properties for forces, velocities, textures... all of which control the
appearance and behaviour of the particles emitted. A complete run down of these settings
is beyond the scope of the tutorial, but they are described in detail in the <a href="https://github.com/fallahn/xygine/wiki" target="_blank">xygine documentation</a>. In the <a href="https://github.com/fallahn/xygine" target="_blank">xygine repository</a> there is a project named <code>SimpleParticleEditor</code>
which can be used to adjust the settings of a particle emitter using a UI and provides
real time feedback, useful for designing new effects. It also allows saving the settings
to a .xyp file, which is not disimilar to the <code>SpriteSheet</code> .spt format. Using this tool
I have created a particle settings file for this tutorial, which can be loaded with
<code>xy::EmitterSettings::loadFromFile()</code> at runtime.<br />
<br />
The <code>ParticleSystem</code> class is used to render the <code>ParticleEmitter</code> component. Particles
are unique as renderables in that they do not require a <code>Drawable</code> component, and are
therefore not drawn by the <code>RenderSystem</code>. This is due to the fact that they use a
special GPU assisted render path. When adding the <code>ParticleSystem</code> to <code>m_gameScene</code> it
needs to be added <i>after</i> the <code>xy::RenderSystem</code>, so that particles are drawn on top of
everything else. While this does provide the benefit of GPU accelerated particles, it has
the drawback of not being depth sorted with other <code>Drawable</code> entities, unfortunately.<br />
<br />
<hr />
<h3>
Creating the FXDirector</h3>
So now we're familiar with the components we want to use and the <code>AudioSystem</code> and
<code>ParticleSystem</code> have been added to the <code>Scene</code>, we can start crafting our <code>FXDirector</code>.
The idea is this: listen for <code>BlockEvent</code> messages in the message handler, then when we
see a <code>Block</code> being destroyed dispatch an entity with an <code>AudioEmitter</code> and
<code>ParticleEmitter</code> attached to it to play their effects at the position held in the the
<code>BlockEvent</code> message.<br />
<br />
Create a new header file in the include directory and call it <code>FXDirector.hpp</code>. Update
the CMake file if necessary. Include<br />
<br />
<pre><code>xyginext/ecs/Director.hpp</code></pre>
<pre><code> </code></pre>
so our class can inherit the <code>Director</code> interface. The declaration of <code>FXDirector</code> looks
like this:<br />
<br />
<pre><code>class FXDirector final : public xy::Director
{
public:
explicit FXDirector(xy::ResourceHandler&);
void handleEvent(const sf::Event&) override {}
void handleMessage(const xy::Message&) override;
void process(float) override;
private:
xy::ResourceHandler& m_resources;
xy::EmitterSettings m_particleSettings;
std::vector<xy::Entity> m_entities;
std::size_t m_nextFreeEntity;
xy::Entity getNextFreeEntity();
void resizeEntities(std::size_t);
void doEffect(sf::Vector2f);
};</code></pre>
<pre><code> </code></pre>
The class is going to be constructed with a reference to a <code>ResourceHandler</code>, which
we'll pass in from the <code>GameState</code>. This is because we'll load our resources for the
sound and particles in the <code>FXDirector</code> constructor.<br />
<br />
The three public functions implement the <code>Director</code> interface. We're not using the event
handler, so this has a default empty body.<br />
Rather than create a new entity every time the <code>FXDirector</code> dispatches one we'll take a
pooling approach, so we have a <code>std::vector<xy::Entity></code> to act as our pool and some
book keeping functions to track free entities.<br />
<br />
Add a new file to your project called FXDirector.cpp and update the CMake file. At the top
include FXDirector.hpp plus any other headers required.<br />
<br />
<pre><code>#include "MessageIDs.hpp"
#include <xyginext/resources/ResourceHandler.hpp>
#include <xyginext/ecs/components/Transform.hpp>
#include <xyginext/ecs/components/AudioEmitter.hpp>
#include <xyginext/ecs/Scene.hpp></code></pre>
<pre><code> </code></pre>
We'll need to be able to identify incoming messages, as well as set up the relevant
components and entities.<br />
In the constructor let's load the required resources.<br />
<br />
<pre><code>FXDirector::FXDirector(xy::ResourceHandler& rh)
: m_resources (rh),
m_nextFreeEntity (0)
{
m_particleSettings.loadFromFile("assets/particles/impact.xyp", rh);
AudioBufferHandle = rh.load<sf::SoundBuffer>("assets/sound/boop.wav");
}</code></pre>
<pre><code> </code></pre>
The particle settings struct can load the settings file directly from disk. It also
takes a reference to the <code>ResourceHandler</code>, as it will be needed to load any textures
required by the particle system. <code>AudioBufferHandle</code> is used to store the handle we get
from the <code>ResourceHandler</code> and is placed in an anonymous namespace at the top of the
.cpp file. We'll be using it to assign the <code>sf::SoundBuffer</code> to the <code>AudioEmitter</code>
components.<br />
<br />
<pre><code>void FXDirector::handleMessage(const xy::Message& msg)
{
if (msg.id == MessageID::BlockMessage)
{
const auto& data = msg.getData<BlockEvent>();
if (data.action == BlockEvent::Destroyed)
{
doEffect(data.position);
}
}
}</code></pre>
<pre><code> </code></pre>
The message handler picks up any incoming <code>BlockEvent</code> messages, and when a <code>Block</code> is
destroyed uses <code>doEffect()</code> to dispatch an entity to the correct position. As an
exercise I encourage you to expand on this later with messages raised by the ball
bouncing off the walls to play a different sound.<br />
<br />
<pre><code>void FXDirector::process(float)
{
//check all entities and free any which have finished playing all the effects
for (auto i = 0u; i < m_nextFreeEntity; ++i)
{
if (m_entities[i].getComponent<xy::AudioEmitter>().getStatus() == xy::AudioEmitter::Stopped
&& m_entities[i].getComponent<xy::ParticleEmitter>().stopped())
{
//swaps the expired entity with the last active entity
//and decrements i so we're up to date in the loop
auto entity = m_entities[i];
m_nextFreeEntity--;
m_entities[i] = m_entities[m_nextFreeEntity];
m_entities[m_nextFreeEntity] = entity;
i--;
}
}
}</code></pre>
<pre><code> </code></pre>
The <code>process()</code> function loops over the entity pool and checks the status of any playing
effects. If an effect has completely stopped the inactive entity is re-pooled and the
active count updated.<br />
<br />
<pre><code>xy::Entity FXDirector::getNextFreeEntity()
{
if (m_nextFreeEntity == m_entities.size())
{
resizeEntities(m_entities.size() + MinEntities);
}
return m_entities[m_nextFreeEntity++];
}
void FXDirector::resizeEntities(std::size_t size)
{
auto currSize = m_entities.size();
m_entities.resize(size);
for (auto i = currSize; i < size; ++i)
{
m_entities[i] = getScene().createEntity();
m_entities[i].addComponent<xy::Transform>();
m_entities[i].addComponent<xy::AudioEmitter>().setSource(m_resources.get<sf::SoundBuffer>(AudioBufferHandle));
m_entities[i].addComponent<xy::ParticleEmitter>().settings = m_particleSettings;
}
}</code></pre>
<pre><code> </code></pre>
These two functions are used for book keeping of the entity pool. The first finds the
next available entity and returns it. If the pool currently has no free entities
then the function below is called to resize the pool. This is where entities are
actually created, adding the <code>Transform</code>, <code>AudioEmitter</code> and <code>ParticleEmitter</code>
components. Note that here the audio source is applied to the <code>AudioEmitter</code> and the
particle settings are applied to the <code>ParticleEmitter</code>.<br />
<br />
<pre><code>void FXDirector::doEffect(sf::Vector2f position)
{
auto entity = getNextFreeEntity();
entity.getComponent<xy::Transform>().setPosition(position);
entity.getComponent<xy::AudioEmitter>().play();
entity.getComponent<xy::ParticleEmitter>().start();
}</code></pre>
<pre><code> </code></pre>
Finally <code>doEffect()</code> requests the next free entity from the pool, applies the position
and starts the effects.<br />
<br />
To use the <code>FXDirector</code> in the <code>Scene</code> it needs to be added similarly to a <code>System</code>
class. In <code>GameState::createScene()</code> underneath where the last System is added to
<code>m_gameScene</code> add<br />
<br />
<pre><code>m_gameScene.addDirector<FXDirector>(m_resources);</code></pre>
<pre><code> </code></pre>
Don't forget that <code>m_resources</code> needs to be passed to the function so that it is
correctly forwarded to the <code>FXDirector</code> constructor.<br />
<br />
That's it! Build and run the game, and when a <code>Block</code> is destroyed you should now see a
small shower of sparks and hear a sound play!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg5tUcZK8fdGT3lSfPDGMCNHnt0M-3YODYRrBNmJtZjXjnYugpl5g91LG2AjSB-8U4Btb7wlSTyAMCycg7IY7x9_4CMfLrDs6cHHCGTV5RYyzS5JEceSHESFj5_1DxGm3vN4Q1Y7LraYg/s1600/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg5tUcZK8fdGT3lSfPDGMCNHnt0M-3YODYRrBNmJtZjXjnYugpl5g91LG2AjSB-8U4Btb7wlSTyAMCycg7IY7x9_4CMfLrDs6cHHCGTV5RYyzS5JEceSHESFj5_1DxGm3vN4Q1Y7LraYg/s320/screenshot.png" width="320" /></a></div>
<br />
<br />
<hr />
<h3>
Taking it further</h3>
While this is the end of the tutorial, it is by no means the end of what can be
achieved with xygine. The documentation <a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a> covers
many more features that are available, a few of which are:<br />
<ul>
<li>AudioScapes. Analogous to <code>SpriteSheets</code> these are text based configuration files
used to define <code>AudioEmitter</code> settings outside of xygine. For an example of how
these are used see the <a href="https://github.com/fallahn/xygine/tree/master/Demo" target="_blank">Demo application</a> in the xygine repository.</li>
<li>Post Process effects. Shaders haven't been covered at all in this tutorial but they
are available in xygine. If they are compatible with SFML then they can be applied
any <code>xy::Drawable</code> component, and Scene-wide via the <code>PostProcess</code> class. xygine
contains a few built in <code>PostProcess</code> effects that can be added to the <code>Scene</code> via
<code>Scene::addPostProcess<T>()</code>, and of course you can implement your own.</li>
<li>Animated Sprites. <code>xy::Sprite</code> fully supports multiframe animation via the
<code>SpriteAnimator</code> system, in combination with the <code>SpriteAnimation</code> component and
metadata provided by the <code>SpriteSheet</code> format.</li>
<li>Networking. xygine has complete network support via the Enet game networking library.
The <a href="https://github.com/fallahn/xygine/tree/master/Demo" target="_blank">Demo application</a> in the xygine repository implements a 2 player online network
mode by way of example.</li>
<li>Utility functions for vector maths, strings and random numbers. The <code>xy::Utility</code>
namespace contains various functions for operating on 2D vectors such as normalise
and dot product, as well as string functions for UTF encoding and random number generators
which can be seeded with custom values.</li>
<li>Optional extras. The <a href="https://github.com/fallahn/xygine/tree/master/xyginext/extras" target="_blank">extras directory</a> in the xygine repository contains useful other
classes such as a <code>PhysicsSystem</code> to bind chipmunk2D to xygine for physics
simulation. It also has networking utilities and prototype drawing functions for
simple lines and shapes.</li>
</ul>
Thank you for making it to the end of this tutorial! I hope xygine is useful to anyone wanting to make games with C++ and SFML (and I'd love to see anything you make with it!). If you have any comments or feedback about this tutorial, or anything else about xygine, please feel free to post on the github repository page.<br />
<a href="https://github.com/fallahn/xygine/">https://github.com/fallahn/xygine/</a>Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-60695464390643855392019-10-04T19:00:00.000+01:002019-10-04T19:00:03.023+01:00Getting started with xygine - Part 5<h3>
Overview</h3>
In <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-4.html">part four</a> of the tutorial I left the task of creating block entities as an exercise
for the reader, so I'll not be covering the code here. If you haven't added the blocks
yet the <a href="https://github.com/fallahn/xygine/tree/master/tutorial/part_5">source code</a> for this part of the tutorial includes the block code in
<code>GameState::createBlocks()</code>. From here I'm going to assume the block code is functioning
as we concentrate on tidying up the assets with a <code>SpriteSheet</code> and implementing a game
UI overlay.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji7RMD8Guj66qpKUcTXAjkdOg3L8OLJ3xERBvcnDWRskpeLob94TeMPWlpsXnXbFtumPYk6yseYub3CBlRd3pFI49dqO6h8ORDuUKPW7ZnTcD4PbkZGSB_Hp8EIkRn71tQWuYwp2BZusk/s1600/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji7RMD8Guj66qpKUcTXAjkdOg3L8OLJ3xERBvcnDWRskpeLob94TeMPWlpsXnXbFtumPYk6yseYub3CBlRd3pFI49dqO6h8ORDuUKPW7ZnTcD4PbkZGSB_Hp8EIkRn71tQWuYwp2BZusk/s320/screenshot.png" width="320" /></a></div>
<br />
<h3>
Sprite Sheets</h3>
In <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-3.html" target="_blank">part three</a> we refactored asset management so that we are now using an instance of
<code>xy::ResourceHandler</code> to book keep the textures in use. However, rather than loading
multiple textures it is possible, using <code>xy::SpriteSheet</code>, to merge the textures into a
single file, a texture atlas, and load a text-based configuration file describing the
<code>Sprite</code>s contained within. The assets directory for this part of the tutorial now
contains a single image, composed of the <code>Paddle</code> and <code>Ball</code> textures and also a
texture for the blocks. In the <code>assets/spritesheets</code> directory there is also a text file
named <code>sprites.spt</code>. The <code>.spt</code> extension is used to denote sprite sheets used with
xygine, although the format of the text file is the same <code>xy::ConfigFile</code> format used by
all xygine description files, such as particle systems. More about the <code>xy::ConfigFile</code>
format can be found in the <a href="https://fallahn.github.io/xyDocs/" target="_blank">documentation</a>, but for now know that the <code>.spt</code> file contains
information about the image source, sprite names from which the image is composed, sub-regions
describing the area of the atlas used by a specific sprite, as well as having the option
to describe animation data used by animated sprites. This system allows easy editing of
sprite data externally from xygine, without having to recompile the game each time an
animation is changed or sprite atlas modified.<br />
<br />
To use the <code>SpriteSheet</code> in the game first rename the <code>TextureID</code> namespace to
<code>SpriteID</code> in Resources.hpp. We're going to use the same technique as managing each
texture, but instead apply it to the sprites. Add a new member to the enum for the
<code>Block</code> sprite, and modify the handles array so that it is an array of <code>xy::Sprite</code>
rather than <code>std::size_t</code>.<br />
<br />
In <code>GameState::loadAssets()</code> where we previously loaded the textures, modify the code to
use an instance of <code>xy::SpriteSheet</code>. You'll have to include the header<br />
<br />
<pre><code>xyginext/graphics/SpriteSheet.hpp</code></pre>
<pre><code> </code></pre>
The <code>SpriteSheet</code> class can be used to load multiple <code>.spt</code> files from disk as we'll be
copying the sprite data out of it and letting it drop out of scope when we're done.<br />
<br />
<pre><code>xy::SpriteSheet spriteSheet;
spriteSheet.loadFromFile("assets/sprites/sprites.spt", m_resources);</code></pre>
<pre><code> </code></pre>
Note that a reference to the resource manager is passed to the function, so that any
loaded textures are still kept by the <code>ResourceHandler</code> as usual.<br />
<br />
When the sprite sheet is loaded (<code>xy::SpriteSheet::loadFromFile()</code> returns true on
success) we can grab the loaded sprite data and store it in the <code>SpriteID</code> array<br />
<br />
<pre><code>SpriteID::sprites[SpriteID::Ball] = spriteSheet.getSprite("Ball");
SpriteID::sprites[SpriteID::Paddle] = spriteSheet.getSprite("Paddle");
SpriteID::sprites[SpriteID::Block] = spriteSheet.getSprite("Block");</code></pre>
<pre><code> </code></pre>
That's pretty much it. Whenever you need to create a new entity with a sprite component
preloaded sprites can easily be fetched from the array. For example the <code>Paddle</code> and
<code>Ball</code> entities will now need to be modified:<br />
<br />
<pre><code>paddle.ball.addComponent<xy::Sprite>() = SpriteID::sprites[SpriteID::Ball];</code></pre>
<pre><code> </code></pre>
For more information on the <code>SpriteSheet</code> class and how to handle animated sprites, see
the xygine <a href="https://fallahn.github.io/xyDocs/" target="_blank">documentation</a>, or review the source of the demo game included in the <a href="https://github.com/fallahn/xygine/tree/master/Demo" target="_blank">xygine repository</a>.<br />
<br />
<hr />
<h3>
Adding A UI</h3>
By now the game should be somewhat playable, with the <code>Ball</code> respawning when it goes off
screen, and breakable <code>Blocks</code> to aim at. To flesh out the gameplay ideally we want to
track some stats such as the score and the player's lives, and display them on the
screen. In a small project such as this it would be perfectly acceptable to render a UI
using entities added to the game scene, but for demonstration purposes we'll create a
second <code>Scene</code> in the <code>GameState</code> called <code>m_uiScene</code>. In a larger project, particularly
one with a moving camera, it is often easier to manage items which remain constant in
position with their own <code>Scene</code>. For example following a player around the world
shouldn't modify the appearance of an inventory or health bars, which should remain
static relative to the camera.<br />
<br />
Adding another <code>Scene</code> is as simple as adding a new <code>xy::Scene</code> member to <code>GameState</code>,
right below <code>m_gameScene</code>. Then the new <code>Scene</code> (<code>m_uiScene</code>) needs to forward events,
forward messages, receive updates and be drawn, in exactly the same way as the game
scene. It will also need to be properly constructed in the the initialiser list of
<code>GameState</code>, and the current view applied to its active camera.<br />
<br />
At the bottom of <code>createScene()</code> the new UI scene needs to have the necessary systems
added to it. Right now those are <code>xy::TextSystem</code>, <code>xy::RenderSystem</code> and
<code>xy::CommandSystem</code>. The command system will be used when the text in the UI needs to be
updated. To do this also update the <code>CommandID</code> namespace with two new enum members and
give them the correct values<br />
<br />
<pre><code>namespace CommandID
{
enum
{
Paddle = 0x1,
Score = 0x2,
Lives = 0x4
};
}</code></pre>
<pre><code> </code></pre>
Remember that these values need to double each time as <code>CommandID</code>'s allow OR'ing
together target IDs.<br />
<br />
Then create two new entities below where we added the systems to the UI scene,
remembering to use the <code>Entity</code> factory function from <code>m_uiScene</code>!<br />
<br />
<pre><code>entity = m_uiScene.createEntity();
entity.addComponent<xy::Transform>().setPosition(20.f, 20.f);
entity.addComponent<xy::Text>(m_resources.get<sf::Font>(FontID::handles[FontID::ScoreFont]));
entity.getComponent<xy::Text>().setCharacterSize(50);
entity.getComponent<xy::Text>().setString("Score: 0");
entity.addComponent<xy::Drawable>();
entity.addComponent<xy::CommandTarget>().ID = CommandID::Score;
entity = m_uiScene.createEntity();
entity.addComponent<xy::Transform>().setPosition(20.f, 60.f);
entity.addComponent<xy::Text>(m_resources.get<sf::Font>(FontID::handles[FontID::ScoreFont]));
entity.getComponent<xy::Text>().setCharacterSize(50);
entity.getComponent<xy::Text>().setString("Lives: 3");
entity.addComponent<xy::Drawable>();
entity.addComponent<xy::CommandTarget>().ID = CommandID::Lives;</code></pre>
<pre><code> </code></pre>
Notice that the resource manager now also handles the font we want to use. Refer back
to <code>MyFirstState</code> to see how fonts are loaded if you modified it to use a
<code>ResourceHandler</code> in part three. It is also the same technique used when loading
textures and storing their resource handles, only within a <code>FontID</code> namespace instead of
a <code>TextureID</code>.<br />
<br />
Build and run the game to make sure everything is configured correctly, and that the
<code>Score</code> and <code>Lives</code> text appears in the top left corner of the screen. If not, check
that the UI scene is correctly added to <code>handleEvent()</code>, <code>handleMessage()</code>, <code>update()</code>
and <code>draw()</code>.<br />
<h3>
Updating the UI</h3>
In part four we covered using the <code>MessageBus</code> to raise messages if the <code>Ball</code> went
out of play, and using the message handler to spawn a new <code>Ball</code> when it did. Hopefully,
when creating the <code>Block</code> entities, you employed this technique to raise a
<code>BlockEvent</code> message when a <code>Block</code> is destroyed. If not, reviewing the <a href="https://github.com/fallahn/xygine/tree/master/tutorial/part_5" target="_blank">source in the repository</a> if necessary, add that now. We want to be able to receive a message each time
a new <code>Block</code> is created, and each time one is destroyed.<br />
<br />
With the messages in place add two new members to <code>GameState</code><br />
<br />
<pre><code>std::size_t m_score;
std::size_t m_lives;</code></pre>
<pre><code> </code></pre>
Initialise these to 0 and 3 respectively. Now in <code>handleMessage()</code> these can be updated
appropriately: if we get a message saying the <code>Ball</code> despawned, reduce the player's
lives by one. Only respawn the <code>Ball</code> if the lives are not at 0. If we get a message
saying a <code>Block</code> was broken increase the score. For the example I've incremented the
score by 100 each time, but feel free to experiment with the scoring system. For instance
the amount of points awarded could increase with each block broken before the <code>Ball</code>
hits the <code>Paddle</code> again.<br />
<br />
Updating the score display from within the message handler is simple, using the <code>CommandSystem</code>
we added to <code>m_uiScene</code>.<br />
<br />
<pre><code>xy::Command cmd;
cmd.targetFlags = CommandID::Score;
cmd.action = [&](xy::Entity e, float)
{
e.getComponent<xy::Text>().setString("Score: " + std::to_string(m_score));
};
m_uiScene.getSystem<xy::CommandSystem>().sendCommand(cmd);</code></pre>
<pre><code> </code></pre>
Use the same technique to update the <code>Lives</code> display. Remember to send the command via
<code>m_uiSystem</code>, not <code>m_gameSystem</code>!<br />
<br />
<hr />
<h3>
Summary</h3>
From here develop your own game rules. Track the lives value and when it reaches zero,
display a game over message. Track the count of active blocks and when it reaches zero
spawn a new set. Try creating blocks in a different pattern.<br />
<br />
For an even more fully fleshed out experience refer to <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-2.html" target="_blank">part two</a> of the tutorial where we
created a new <code>State</code>, and use it to create a <code>PauseState</code>. Add a menu to the <code>PauseState</code>,
using <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-1.html" target="_blank">part one</a> of the tutorial as a guide, so that the player can quit or restart the
game. Remember you only have to push the <code>PauseState</code> on to the <code>StateStack</code> to pause the
game and pop it again to resume play.<br />
<br />
Maybe even try spawning special entities when random <code>Block</code>s break, and award bonuses
if the player catches them with the <code>Paddle</code>.<br />
<br />
If you made it this far, thank you for staying with me! In the last part we'll cover the
<code>Director</code> class and use it to create special effects with sounds and particles.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-74866134675994606092019-09-27T19:00:00.000+01:002019-09-27T19:00:07.283+01:00Getting started with xygine - Part 4<h3>
Before we begin</h3>
In this part of the tutorial we'll be looking at how to implement collision in the breakout
game we've been creating with xygine. Collision tends to be one of the more complex parts
of game development and, as such, this part of the tutorial may become a bit wordy.
Creating collisions requires only two xygine systems, one of which already comes with
xygine, but they require some explanation. Because of this I've tried to cut back on
explanation of other code, such as adding new graphical entities, as these are already
covered in the <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-3.html" target="_blank">previous parts</a> of the tutorial. The collision process is based on a blog
post I wrote some time ago, which you can read here:
<a href="https://trederia.blogspot.com/2016/02/2d-physics-101-pong.html" target="_blank">https://trederia.blogspot.com/2016/02/2d-physics-101-pong.html </a>
I'll try to refrain from reiterating anything too much and stick to explaining the actual
implementation - so I highly recommend reading the blog post first to give you some idea of
what we're trying to do.<br />
<h3>
Setting up</h3>
In the previous paragraph I mentioned that there are only two systems needed for collision.
Strictly speaking there's only one which is really necessary, but xygine includes a spatial
partitioning system which can come in handy for optimising collision detection. The
<code>DynamicTreeSystem</code>, as it's known, is an implementation of the balanced AABB tree based on
the source of Box2D (and, to some extent, bullet physics.
See <a href="https://github.com/fallahn/xygine/blob/master/xyginext/include/xyginext/ecs/systems/DynamicTreeSystem.hpp#L68">here</a>
for more info). While it may be a slight 'over-optimisation' for our use case I'm including
it because it's worth knowing about when scaling up a xygine project to something more
complex.<br />
<br />
Firstly, then, add the include directive for the <code>DynamicTreeSystem</code> to <code>GameState</code>, and
add it to the list of <code>Scene</code> systems in <code>createScene()</code><br />
<br />
<pre><code>xyginext/ecs/systems/DynamicTreeSystem.hpp</code></pre>
<pre><code> </code></pre>
The matching component for this system is called the <code>BroadphaseComponent</code> as it is
generally used in the 'broad phase' or first-pass of culling items from the list of
potential collisions.<br />
<br />
<pre><code>xyginext/ecs/components/BroadphaseComponent.hpp</code></pre>
<pre><code> </code></pre>
This component has one property in particular which can also be useful in the soon-to-be
created <code>CollisionSystem</code>, namely an AABB (axis aligned bounding box) in the form of an
<code>sf::FloatRect</code>. This AABB is used by the dynamic tree to partition the <code>Scene</code>, and quickly
retrieve only other AABBs that are nearby.<br />
We'll come back to deploying the <code>BroadphaseComponent</code>s, but before that let's start on the
<code>CollisionSystem</code>.<br />
<h3>
Collision System Overview</h3>
The <code>CollisionSystem</code> requires much the same initial set up as our <code>BallSystem</code>. We'll add
<code>CollisionSystem.hpp</code> and <code>CollisionSystem.cpp</code> to the relevant directories, and update the
CMake files. The header is used to declare the <code>Collider</code> component as well as the
<code>CollisionSystem</code> which, as before, inherits <code>xy::System</code> and implements the <code>process()</code>
function.<br />
The collider component is a simple struct with two members:<br />
<br />
<pre><code>struct Collider final
{
bool dynamic = false;
std::function<void(xy::Entity, xy::Entity, Manifold)> callback;
};</code></pre>
<pre><code> </code></pre>
The <code>dynamic</code> bool is used to indicate if the collidable moves, or is static geometry. In the
game the only moving object is the ball - while the paddle is moved by the
mouse it's not considered 'dynamic' as it doesn't react to collisions.<br />
The <code>std::function</code> is a little more complicated. Here we optionally add a callback
function, which is called at the end of a collision, if it exists. For example once a
collision has been resolved and the objects are no longer overlapping, calling this on the
ball should execute a function updating the velocity so that it bounces off of solid
objects. The callback takes three parameters - the entity whose collision was just
resolved, the entity with which the first entity just collided, and a copy of the
collision manifold. For an explanation of the manifold I recommend reading
<a href="https://trederia.blogspot.com/2016/02/2d-physics-101-pong.html">this</a> blog post about
collisions. The declaration of the manifold struct is also in CollisionSystem.hpp<br />
<br />
As with the <code>BallSystem</code> the <code>CollisionSystem</code> has two parts to its definition. In the
constructor the filter is set up to declare which entities the <code>CollisionSystem</code> is
interested in<br />
<br />
<pre><code>CollisionSystem::CollisionSystem(xy::MessageBus& mb)
: xy::System(mb, typeid(CollisionSystem))
{
requireComponent<xy::Transform>();
requireComponent<xy::BroadphaseComponent>();
requireComponent<Collider>();
}</code></pre>
<pre><code> </code></pre>
Notice that we're requiring the <code>BroadphaseComponent</code> be present on the entities. The
<code>process()</code> function is quite a bit lengthier than that of the <code>BallSystem</code>, so rather
than list the entire code (<a href="https://github.com/fallahn/xygine/tree/master/tutorial/part_4" target="_blank">the source of which you can find in the repository</a>) I'll outline
the steps it performs:<br />
First, as with most systems, get the list of active entities with <code>getEntities()</code> and then
start iterating over it.<br />
For each entity get the <code>Collider</code> component and check if it's dynamic. We can ignore
static <code>Colliders</code> as they won't move or create new collisions with other static geometry.
If it is dynamic, however, then let's check to see if it collided with anything.<br />
<br />
This is where the <code>BroadphaseComponent</code> comes in (in fact we're performing the broad phase
right here!) - taking the AABB of the dynamic object's <code>BroadphaseComponent</code> we can query
the Scene's <code>DynamicTreeSystem</code>, which returns a list of entities that are <i>close by</i> the
dynamic object, saving us from having to check every single object in the <code>Scene</code>.<br />
<br />
<pre><code>auto others = getScene()->getSystem<xy::DynamicTreeSystem>().query(bounds);
</code></pre>
<br />
With this list of entities in hand, next check the AABB of each one to see if it intersects
with the dynamic object's AABB. If it does, insert it into a
<code>std::set<std::pair<xy::Entity, xy::Entity>> m_collisions</code>, which is a member of <code>CollisionSystem</code>.<br />
<br />
<pre><code>m_collisions.insert(std::minmax(entity, other));</code></pre>
<pre><code> </code></pre>
I'm using <code>std::set</code> as it will make sure that each collision pair (when sorted with
<code>minmax()</code>) is inserted only once. Otherwise the same collision would be calculated twice,
once from the perspective of each entity in the collision.<br />
Once the broad phase is complete and the entire list of entities has been iterated over
we're left with a set of colliding pairs. These are now operated on by iterating over the
set in a 'narrow phase'. For each pair in <code>m_collisions</code> the overlap is calculated and used to create the
manifold. If either of the pair are dynamic then the position of the dynamic object is corrected so that neither
of the objects are overlapping. Finally, if there is a callback attached, the callback is
executed for each of the colliders.<br />
<br />
Phew.<br />
<br />
<hr />
<h3>
Deploying the CollisionSystem</h3>
With the system complete it's time to start hooking it up to the <code>Scene</code>. Include the header
file in GameState.cpp and add an instance to the <code>Scene</code> in <code>createScene()</code>. When adding the
<code>CollisionSystem</code> to the <code>Scene</code> it's probably worth making sure that it's added right
after the <code>DynamicTreeSystem</code>, so that when the <code>CollisionSystem</code> is processed and it
queries the <code>DynamicTreeSystem</code> we're sure the <code>DynamicTreeSystem</code> is up to date.<br />
<br />
For the paddle and ball to collide we need to add both a <code>BroadphaseComponent</code> and
<code>Collider</code> to each of them. Starting with the paddle add one of each:<br />
<br />
<pre><code>entity.addComponent<xy::BroadphaseComponent>(paddleBounds);
entity.addComponent<Collider>();</code></pre>
<pre><code> </code></pre>
The AABB is set on the <code>BroadphaseComponent</code> using the size of the <code>Sprite</code>, which we
handily already have in <code>paddleBounds</code>. In <code>spawnBall()</code> do the same, again using the <code>Sprite</code> size for the
AABB bounds. Crucially here, however, we add a callback to the collider so that the <code>Ball</code>
knows how to behave once it has collided.<br />
<br />
<pre><code>paddle.ball.addComponent<Collider>().callback =
[](xy::Entity e, xy::Entity other, Manifold man)
{
//if we hit the paddle change the velocity angle
if (other.hasComponent<Paddle>())
{
auto newVel = e.getComponent<xy::Transform>().getPosition() - other.getComponent<xy::Transform>().getPosition();
e.getComponent<Ball>().velocity = xy::Util::Vector::normalise(newVel);
}
else
{
//reflect the ball's velocity around the collision normal
auto vel = e.getComponent<Ball>().velocity;
vel = xy::Util::Vector::reflect(vel, man.normal);
e.getComponent<Ball>().velocity = vel;
}
};</code></pre>
<pre><code> </code></pre>
The callback checks to see if the ball collided with a paddle by testing the second entity
for a <code>Paddle</code> component. If it finds one then the ball's velocity is set to that of the
collision angle. This gives us the classic behaviour of skewing the bounce angle the
further the ball is from the centre of the paddle. If the ball has collided with something
else, say a wall or block, then the collision manifold is used to reflect the ball's
velocity. The vector functions used here are found in xygine's <code>Utility</code> namespace, and are
documented on the <a href="https://github.com/fallahn/xygine/wiki" target="_blank">wiki page</a>.<br />
<br />
NOTE: The ball is not immediately set as a dynamic collider, as it's not considered dynamic
while is sits on the paddle. In the event handler, which launches the ball on a button
press, the <code>Collider</code> is also updated and its 'dynamic' property set to true.<br />
<br />
Currently we can't really test the collision while the ball is free to fly out of
the arena. In <code>createScene()</code>, below where the paddle entity is created, create three new
entities, one for the top wall, and two for the left and right sides. These entities need
only a <code>Transform</code> component along with the <code>Broadphase</code> and <code>Collider</code> components to
function. The <code>xy::DefaultSceneSize</code> constant also comes in handy here when calculating the
AABBs for the three entities. Of course it would be nice to also see these entities, which
can be done by adding sprites with new textures added to the <code>TextureID</code> namespace.
Alternatively the '<a href="https://github.com/fallahn/xygine/tree/master/xyginext/extras" target="_blank">extras</a>' directory in the xygine repository contains a header file called
<code>ShapeUtils.hpp</code>. Adding this to your project will provide functions for quickly creating
rectangle or circle shapes which are useful for prototyping. The wall entities need only a
<code>Drawable</code> component added for this:<br />
<br />
<pre><code>Shape::setRectangle(entity.addComponent<xy::Drawable>(), { wallBounds.width, wallBounds.height });
</code></pre>
<br />
Now we're finally in a position to build and run the project to see the fruits of our labour.
Running the game will give us the paddle and ball from before, but now when clicking the
mouse the ball should move until it hits the top wall, and bounce off. Hitting the ball
with different parts of the paddle will change its velocity angle. Awesome!<br />
<br />
<hr />
<h3>
Adding some polish with Messages</h3>
By now you've probably noticed that it's possible to spawn many balls at once by repeatedly
clicking the mouse button. To stop this is pretty simple: go to <code>handleEvent()</code> and in the
mouse button handler remove<br />
<br />
<pre><code>else
{
spawnBall();
}</code></pre>
<pre><code> </code></pre>
This doesn't help, however, when we lose the ball off the screen. To spawn a new
ball as soon as the old one is lost, we can use xygine's <code>MessageBus</code>. The <code>MessageBus</code> is
an application-wide list of messages which behave very much like Events, but can have
arbitrary data placed on it from any class which can see it. This is why a reference to the
<code>MessageBus</code> is passed to all <code>System</code> classes when they are created.<br />
<br />
More detail of the <code>MessageBus</code> can be found in the xygine documentation, and on the <a href="https://github.com/fallahn/xygine/wiki/Messages" target="_blank">wiki page</a>, but all we need to know right now is that every message type needs a unique ID to
identify it, and a struct declaration to state what kind of data the message holds. It is
IMPORTANT that messages be kept small, however, and data be kept to POD such as numeric
values and pointers. The maximum size of a message is 128 bytes.<br />
We'll create a new message type used to notify the rest of the game when a ball is spawned
and despawned. Any part of the game listening to these messages can then perform actions
such as playing a sound or reducing the player's ball count. We can also use it to spawn a
new ball.<br />
<br />
Create a new header file called <code>MessageIDs.hpp</code> and add a reference to it to the relevant
CMake file. Include the xygine <code>Message</code> header<br />
<br />
<pre><code>xygine/core/Message.hpp</code></pre>
<pre><code> </code></pre>
and add a new namespace to contain the message IDs<br />
<br />
<pre><code>namespace MessageID
{
enum
{
BallMessage = xy::Message::Count
};
}</code></pre>
<pre><code> </code></pre>
Note that the first ID actually has the value of <code>xy::Message::Count</code>. This is very
important as xygine has its own built in IDs, and overwriting any of them will cause no end
of confusing bugs.<br />
Underneath the namespace add a new struct to hold the message data<br />
<br />
<pre><code>struct BallEvent final
{
enum
{
Spawned, Despawned
}action;
sf::Vector2f position;
}</code></pre>
<pre><code> </code></pre>
This should contain everything we'll need elsewhere in the game when the ball is spawned or
removed from the play area.<br />
<br />
In <code>BallSystem::process()</code> find the line which checks to see if the ball has left the play
area. Underneath raise a new message:<br />
<br />
<pre><code>auto* msg = postMessage<BallEvent>(MessageID::BallMessage);
msg->action = BallEvent::Despawned;
msg->position = tx.getPosition();</code></pre>
<pre><code> </code></pre>
That's all it takes to create a new message. Note that <code>postMessage()</code> exists in all
<code>System</code> classes, and takes the event data type (<code>BallEvent</code> in this case) as the template
parameter. <code>MessageID::BallMessage</code> is passed as a function parameter so that the data can be
correctly identified by any message handler that may receive it. The function returns a
pointer to the newly created message so that any information about the event can be filled
out.<br />
<br />
Back in <code>GameState.cpp</code> find the <code>handleMessage()</code> function, and update it with a handler
for our new message type.<br />
<br />
<pre><code>if(msg.id == MessageID::BallMessage)
{
const auto& data = msg.getData<BallEvent>();
if(data.action == BallEvent::Despawned)
{
spawnBall();
}
}</code></pre>
<pre><code> </code></pre>
It's important to first check that the message received is what you're looking for. Calling
<code>Message::getData()</code> with the incorrect template parameter will cause anything from subtle
bugs to a horrible crash. Once you have a reference to the data any of the properties can
be read out, in this case checking the action and spawning a new ball should the existing
ball be despawned.<br />
<br />
That's it! Messages are quite a powerful concept in xygine and you should get used to
raising them for all pertinent events such as collisions, spawning and despawning as it makes
tracking the state of the game much easier.<br />
<br />
<hr />
<h3>
Final Step</h3>
As I stated at the beginning, this tutorial section is somewhat long so
I'm not going to cover creating the blocks explicitly. By now you should know enough about
creating entities, adding sprites, collision data and even the mechanism for destroying
blocks (hint: Collider callback) to be able to implement them on your own. Also try raising
messages when blocks are destroyed as these events will be useful later to track the state
of game, such as the number of remaining blocks or what score to award the player. Good
luck! In the next part of the tutorial I'll be covering the game rules, and drawing the UI.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-7237341354025218162019-09-20T19:00:00.000+01:002019-09-20T19:00:02.745+01:00Getting started with xygine - Part 3<h3>
Overview</h3>
In the last two parts (<a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-1.html" target="_blank">here</a> and <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-2.html" target="_blank">here</a>) we've covered creating a new project, learned how to create
entities which display text and sprites, and started making a breakout style game. If
you run the project you should see a menu, be able to click play, and then be presented
with a paddle on the screen which is controllable with a mouse. If not then you should
review the first two parts of the tutorial before carrying on.<br />
In this part we'll look at how resource management works in xygine, before learning how
to create custom components and systems to add a ball. The source code for the tutorial is available <a href="https://github.com/fallahn/xygine/tree/master/tutorial" target="_blank">here</a>.<br />
<h3>
Resources</h3>
In the last part, to get the paddle rendering on the screen, we added an <code>sf::Texture</code>
member to the <code>GameState</code>, <code>m_paddleTexture</code> which is used to draw the paddle. As we'll
be progressively be adding more textures from now on, it's time to do a small amount of
refactoring. xygine has a <code>ResourceHandler</code> class which provides an interface for
loading multiple types of SFML resources such as fonts, textures, images and even sound
buffers. Shaders are, however, a special case and won't be covered in this tutorial. The
<code>ResourceHandler</code> is designed to ensure that only a single instance of any resource
exists at one time and provides fast lookup of loaded resources via an integer ID. It
also provides a fallback mechanism allowing you to handle any errors which may occur
should loading a resource fail. For now we'll be using it to load all of the textures
required for the game.<br />
<br />
When a resource is loaded the <code>ResourceHandler</code> returns a unique ID for that resource,
which can be used to quickly retrieve it when it is needed at run time. A little book
keeping is needed then, to store these IDs.<br />
<br />
Create a new file in the include directory named <code>ResourceIDs.hpp</code>. Make sure to add it
to the CMakeLists.txt file if you're using the CMake build system. Inside the new file
add a namespace named <code>TextureID</code> and an anonymous enum like so:<br />
<br />
<pre><code>namespace TextureID
{
enum
{
Paddle,
Ball,
Count
};
static std::array<std::size_t, TextureID::Count> handles = {};
}</code></pre>
<pre><code> </code></pre>
Note the <code>std::array</code> named <code>handles</code>. This will hold the IDs returned from the resource
manager, and will be indexed by the enum values above it. From now on whenever we add a
new texture to the game we'll create a new member in the enum.<br />
<br />
With this created open GameState.hpp and include the header for the <code>ResourceHandler</code><br />
<br />
<pre><code>xyginext/resources/ResourceHandler.hpp</code></pre>
<pre><code> </code></pre>
and remove the include directive for <code>sf::Texture</code>. Below that remove the member
<code>m_paddleTexture</code> and replace it with<br />
<br />
<pre><code>xy::ResourceHandler m_resources;</code></pre>
<pre><code> </code></pre>
Underneath <code>createScene()</code> add a new function, <code>loadResources()</code>. In GameState.cpp
go to the definition of <code>createScene()</code> and find the line where the paddle texture is
loaded. Remove it and replace it with a call to <code>loadResources()</code>.<br />
<br />
Create a definition for <code>loadResources()</code> below. Here we'll load all of our textures and
any other resources up front, and store the ID assigned to them by the resource manager.
Right now we're only using the paddle texture so add the line<br />
<br />
<pre><code>TextureID::handles[TextureID::Paddle] = m_resources.load<sf::Texture>("assets/images/paddle.png");
</code></pre>
<br />
The <code>ResourceHandler::load()</code> function is templated and can be used to load different
kinds of resources. When loading fonts or images the same pattern of namespacing and
handle array can be used.<br />
Back in <code>createScene()</code>, above, modify the line where the <code>Sprite</code> component is added to
the paddle entity so that it uses the new resource manager.<br />
<br />
<pre><code>entity.addComponent<xy::Sprite>(m_resources.get<sf::Texture>(TextureID::handles[TextureID::Paddle]));
</code></pre>
<br />
Build and run the project to check that every thing is OK, you should see the same
paddle sprite as it was before. If you're feeling bold refactor <code>MyFirstState</code> in the
same way, replacing <code>m_font</code> with a <code>ResourceHandler</code>, and use it curate the font used in
the menu.<br />
<br />
<hr />
<h3>
Balls.</h3>
The essence of an ECS is to 'break apart' deeply inherited classes, isolating the data
of an object into smaller groups such as transform data (position, rotation etc) and
render data (texture IDs, render states) and storing it in sets of structs, away from
the logic part of the entity. These structs become our components, providing the ability
to select which sets of data are associated with an entity, while the logic used for
processing is placed within a system. Systems have a filter applied to them which states
the component requirements an entity must have for the system to be interested in it. An
entity may have more components than a system requires - but not less.<br />
<br />
xygine already provides the <code>Transform</code> and <code>Sprite</code> components needed to make a ball
appear on screen, but a <code>Ball</code> needs other data such as velocity and state (is the ball
active? is it waiting to be launched?) which we can encapsulate in a custom struct. To
make the ball move as we expect it also requires some logic to act upon the data stored
in the <code>Ball</code> struct, which can be implemented in a <code>BallSystem</code>. It may seem like a lot
of code for a single ball, but when you consider what it takes to add 1 extra ball, or
100 extra balls, suddenly the ability to scale with no extra code becomes apparent.
Creating any new entity behaviour in xygine more or less follows this procedure.<br />
<h4>
Implementing the ball</h4>
Add a new file to the include directory and call it <code>BallSystem.hpp</code>. Update the
CMakeLists.txt file too if you're using it. We'll add both the component and the
system declarations to the header file as the component declaration is trivial.<br />
<br />
<pre><code>struct Ball final
{
enum class State
{
Waiting, Active
}state = State::Waiting;
sf::Vector2f velocity = {0.f, -1.f};
static constexpr float Speed = 800.f;
};</code></pre>
<pre><code> </code></pre>
This is the <code>Ball</code> component. It contains an enum class declaring its state - when a
ball is sat on the paddle waiting to be launched it'll be in the <code>Waiting</code> state. Once
launched it'll be <code>Active</code>. The velocity is a normalised vector indicating the ball's
current direction. It's initialised so that when it launches it'll move straight up.
Lastly the constexpr value <code>Speed</code> is used to define the base speed of all balls. It
makes more semantic sense to list it here than anywhere else.<br />
<br />
After the component add the declaration of the <code>BallSystem</code>. It needs to publicly
inherit the xygine <code>System</code> base class, and implement some of its interface. At the
very least we need to fill out the <code>process()</code> function, although there are other
virtual functions in <code>System</code> which may be useful. For a full breakdown of these see
the <a href="https://github.com/fallahn/xygine/wiki" target="_blank">xygine documentation or wiki</a>. The <code>System</code> base class also needs to be constructed
with a reference to the active <code>MessageBus</code>, so the <code>BallSystem</code> will need to take that
as a parameter to the constructor.<br />
<br />
<pre><code>class BallSystem final : public xy::System
{
public:
explicit BallSystem(xy::MessageBus&);
void process(float) override;
private:
};</code></pre>
<pre><code> </code></pre>
With these declared, create BallSystem.cpp in the src directory and update the
CMakeLists.txt file. In BallSystem.cpp include the <code>BallSystem</code> header, as well as the
xygine headers for the <code>Transform</code> component and <code>Scene</code> class. These classes will both
be referenced by the system's process function.<br />
<br />
The constructor of the <code>BallSystem</code> requires a couple of special things. First the
initialiser list needs to correctly initialise the base class by forwarding the
reference to the <code>MessageBus</code> passed in through the constructor, as well as providing
the typeid of the <code>BallSystem</code>. This is used internally by xygine when the <code>Scene</code>
processes all of its systems. The body of the constructor is also used to set up the
'filter' by which the system decides which entities it should operate on. In this case
we want to make sure that an entity has a <code>Ball</code> component and a <code>Transform</code> component.
This is because the system will be modifying the position of the ball.<br />
<br />
<pre><code>BallSystem::BallSystem(xy::MessageBus& mb)
: xy::System(mb, typeid(BallSystem))
{
requireComponent<Ball>();
requireComponent<xy::Transform>();
}</code></pre>
<pre><code> </code></pre>
Next the <code>process()</code> function needs to be defined. This function is called once per
frame by the <code>Scene</code> on every active system. The current frame time, or delta time, is
passed in as a float. We'll be using this when moving the ball to make sure it moves
the correct amount each frame.<br />
<br />
<pre><code>void BallSystem::process(float dt)
{
auto& entities = getEntities();
for(auto entity : entities)
{
auto& ball = entity.getComponent<Ball>();
switch(ball.state)
{
default:
case Ball::State::Waiting:
break;
case Ball::State::Active:
{
auto& tx = entity.getComponent<xy::Transform>();
tx.move(ball.velocity * Ball::Speed * dt);
sf::FloatRect bounds(sf::Vector2f(), xy::DefaultSceneSize);
if(!bounds.contains(tx.getPosition()))
{
getScene()->destroyEntity(entity);
}
}
break;
}
}
}</code></pre>
<pre><code> </code></pre>
The function <code>getEntities()</code> returns a vector of entities which are active in the
current system. By taking this and iterating over it, we can update each entity one at
a time. For each entity then, we find its <code>Ball</code> component and check its state. If it is
currently <code>Waiting</code>... do nothing. The <code>Ball</code> is on the <code>Paddle</code>. If the <code>Ball</code> is
<code>Active</code>, on the other hand, then move it by its velocity multiplied by the
<code>Ball::Speed</code> constant multiplied by the frame time.<br />
<br />
After the ball is moved there is a simple check which takes the <code>Scene</code> size and looks
to see if the <code>Ball</code> is still within the <code>Scene</code> area. If it is not the ball entity is
destroyed.<br />
<br />
<i>A side note:</i> it is possible for a <code>System</code> to be made <code>Drawable</code> by inheriting <code>sf::Drawable.</code>
The <code>Scene</code> will attempt to draw all <code>System</code>s in the order in which they are added to
the <code>Scene</code>.<br />
<br />
Before we can start testing though the <code>BallSystem</code> has to be added to the <code>Scene</code>.
Open the GameState.cpp file, include BallSystem.hpp and add an instance of the system
to the <code>Scene</code> in <code>createScene()</code>, on the line before adding the <code>SpriteSystem</code>.<br />
<br />
<pre><code>m_gameScene.addSystem<BallSystem>(messageBus);</code></pre>
<pre><code> </code></pre>
Unlike the paddle the <code>Ball</code> isn't created once in <code>createScene()</code>. When the game runs
we'll want to request multiple balls, so add a new function called <code>spawnBall()</code> to
<code>GameState</code>. Call this immediately after creating the <code>Paddle</code> entity.<br />
<br />
When the ball is spawned into the game it needs to be placed on the the paddle and
follow the paddle around until it is launched. To do this we can create a <code>Paddle</code>
component which references the active <code>Ball</code> entity. In BallSystem.hpp add a new struct<br />
<br />
<pre><code>struct Paddle final
{
xy::Entity ball;
};</code></pre>
<pre><code> </code></pre>
and add an instance of it as a component to the paddle entity in <code>createScene()</code><br />
<br />
<pre><code>entity.addComponent<Paddle>();</code></pre>
<pre><code> </code></pre>
As the <code>Entity</code> class is a handle for entities within the <code>Scene</code> it is also nullable and,
in fact, is null by default. It has a function <code>isValid()</code> which returns true if the
entity contains a valid handle. Using this we can tell whether or not the <code>Paddle</code> has
a <code>Ball</code> entity waiting to be launched, and reset it as necessary with the <code>spawnBall()</code>
function.<br />
<br />
<pre><code>void GameState::spawnBall()
{
xy::Command cmd;
cmd.targetFlags = CommandID::Paddle;
cmd.action = [&](xy::Entity entity, float)
{
auto& paddle = entity.getComponent<Paddle>();
paddle.ball = m_gameScene.createEntity();
paddle.ball.addComponent<xy::Transform>();
paddle.ball.addComponent<xy::Sprite>(m_resources.get<sf::Texture>(TextureID::handles[TextureID::Ball]));
paddle.ball.addComponent<xy::Drawable>();
paddle.ball.addComponent<Ball>();
auto ballBounds = paddle.ball.getComponent<xy::Sprite>().getTextureBounds();
auto paddleBounds = entity.getComponent<xy::Sprite>().getTextureBounds();
paddle.ball.getComponent<xy::Transform>().setOrigin(ballBounds.width / 2.f, ballBounds.height / 2.f);
paddle.ball.getComponent<xy::Transform>().setPosition(paddleBounds.width / 2.f, -ballBounds.height / 2.f);
entity.getComponent<xy::Transform>().addChild(paddle.ball.getComponent<xy::Transform>());
};
m_gameScene.getSystem<xy::CommandSystem>().sendCommand(cmd);
}</code></pre>
<pre><code> </code></pre>
The function works by sending a command to the <code>Paddle</code> entity. We set up the
<code>CommandSystem</code> in the previous tutorial to receive player input from the mouse. We can
also use this system to tell the <code>Paddle</code> we want a new <code>Ball</code>, and to immediately attach that
<code>Ball</code> to the <code>Paddle</code>. The <code>Transform</code> component has the option to add 'child'
transforms, which in this case will be the <code>Transform</code> of the <code>Ball</code>. While the <code>Ball</code>
transform is parented to the <code>Paddle</code> transform it will follow the <code>Paddle</code> on screen,
which is what we want while the <code>Ball</code> is waiting to be launched.<br />
<br />
To test this we can modify <code>handleEvent()</code> in <code>GameState</code> so that when the left mouse
button is pressed it checks the <code>Paddle</code> to see if the <code>Ball</code> property is valid. If not
it spawns a new <code>Ball</code>, else if it <i>is</i> valid then set the state of the <code>Ball</code> to
<code>Active</code>, and unparent it from the <code>Paddle</code>.
Add this under where we check the MouseMove event.<br />
<br />
<pre><code>else if(evt.type == sf::Event::MouseButtonReleased)
{
if(evt.mouseButton.button == sf::Mouse::Left)
{
//send a command to the paddle to launch the ball if it has one
//else spawn a new ball
xy::Command cmd;
cmd.targetFlags = CommandID::Paddle;
cmd.action = [&](xy::Entity entity, float)
{
auto& paddle = entity.getComponent<Paddle>();
if(paddle.ball.isValid())
{
paddle.ball.getComponent<Ball>().state = Ball::State::Active;
auto ballBounds = paddle.ball.getComponent<xy::Sprite>().getTextureBounds();
paddle.ball.getComponent<xy::Transform>().setPosition(entity.getComponent<xy::Transform>().getPosition() + sf::Vector2f(0.f, -ballBounds.height / 2.f));
entity.getComponent<xy::Transform>().removeChild(paddle.ball.getComponent<xy::Transform>());
paddle.ball = {};
}
else
{
spawnBall();
}
};
m_gameScene.getSystem<xy::CommandSystem>().sendCommand(cmd);
}
}</code></pre>
<pre><code> </code></pre>
Again we're using the <code>CommandSystem</code> to address the <code>Paddle</code> directly. Note also that
when unparenting a <code>Ball</code> from the <code>Paddle</code> that the ball's position needs to be
updated. While parented a <code>Transform</code>'s coordinates are relative to the parent (in this
case the <code>Paddle</code>), so when unparenting the <code>Ball</code> its position needs to be set in world
space coordinates. Finally, when unparenting the <code>Ball</code>, the <code>Paddle</code>'s ball property is
reset with the default constructor, effectively nullifying it.<br />
<br />
If you build and run the project now you should find that first clicking the left mouse
button places a <code>Ball</code> on the paddle, and a second click fires the <code>Ball</code> in a straight
line across the screen.<br />
Now that we have the <code>Ball</code> and <code>Paddle</code> in place we need some basic collision detection
and physics, which will be the topic of the next tutorial.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-86406874684538716412019-09-13T19:00:00.000+01:002019-09-13T19:00:00.353+01:00Getting started with xygine - Part 2<h3>
Recap</h3>
In the <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-1.html">first part</a> of the tutorial we created a basic menu using the <code>MyFirstState</code>
project template. Continuing on from that we'll add a new state, <code>GameState</code>, to host
the game play logic, before taking a look at xygine's <code>Sprite</code> component and the
<code>CommandSystem</code> class. The source for the tutorial can be downloaded <a href="https://github.com/fallahn/xygine/tree/master/tutorial" target="_blank">here</a>.<br />
<h3>
The Game State</h3>
Recalling the last tutorial you should be familiar with the concept of the <code>StateStack</code>
which lives within the <code>Game</code> class. The stack is used to control the active state of
the game, but before we can do that we need to create and register a new <code>GameState</code>
class. All states inherit <code>xy::State</code> but for simplicity's sake rather than look at the
specific <code>State</code> interface (which can be seen in detail in the documentation at
<a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a>) let's start by duplicating MyFirstState.hpp and
MyFirstState.cpp and renaming them to GameState.hpp and GameState.cpp. In GameState.hpp
remove the include line for <code>sf::Font</code>, and remove the <code>m_font</code> member as we won't be
using it. Then rename <code>m_scene</code> to <code>m_gameScene</code> - this is important because later on
there'll be another scene added to contain the user interface. Rename the class from
<code>MyFirstState</code> to <code>GameState</code>. In GameState.cpp the process is similar: rename all
<code>m_scene</code> references to <code>m_gameScene</code> and all <code>MyFirstState</code> references to <code>GameState</code>.
This includes changing the name of the included header file "MyFirstState.hpp" to
"GameState.hpp" at the top of the file. There's no need to include the headers for
the <code>Text</code> component or <code>TextSystem</code> either, but leave the remaining includes as they will
be used later on. The contents of <code>createScene()</code> can be removed too, although you can
leave the lines where the <code>RenderSystem</code> is added, as that'll be used when drawing the
game. The line<br />
<br />
<pre><code>m_scene.getSystem<xy::UISystem>().handleEvent(evt);</code></pre>
<pre><code> </code></pre>
needs to be removed from <code>GameState::handleEvent()</code> as there is no <code>UISystem</code> in use in
the <code>GameState</code>.<br />
<br />
A little book keeping is required if you're using the CMake files to build the tutorial.
In the include directory edit the <code>CMakeLists.txt</code> file and add a new line referencing
GameState.hpp. In the src directory repeat the edit for GameState.cpp.<br />
<br />
To register the state with the game the <code>GameState</code> needs a unique ID to return from the
function <code>GameState::stateID()</code>. Open the States.hpp file and add a new member to the
<code>State</code> enum called <code>GameState</code>. Modify the <code>GameState::stateID()</code> function to return
<code>States::GameState</code>. This is important because the <code>StateStack</code> needs to be able to
identify individual states. With this done open Game.cpp and below the include line
for "MyFirstState.hpp" add an include directive for "GameState.hpp". Then in the
function body of <code>Game::registerStates()</code> add<br />
<br />
<pre><code>m_stateStack.registerState<GameState>(States::GameState);</code></pre>
<pre><code> </code></pre>
This will associate the specific state type (<code>GameState</code>) with its enum value. This way
requesting a new state on the stack can be done with a single ID. Finally, to launch
this new state, we need to modify <code>MyFirstState</code>.<br />
<br />
Open MyFirstState.cpp and go to the definition of <code>createScene()</code>. The process of adding
a new callback is the same as adding the quit button callback, only this time it should
be added to the entity with the text component that says 'Play'. The lambda expression
used in the callback needs to be modified, however, else clicking Play will close the
game! The callback should look like this:<br />
<br />
<pre><code>auto callbackID =
m_scene.getSystem<xy::UISystem>().addMouseButtonCallback(
[&](xy::Entity, sf::Uint64 flags)
{
if(flags & xy::UISystem::LeftMouse)
{
requestStackClear();
requestStackPush(States::GameState);
}
});</code></pre>
<pre><code> </code></pre>
Note that there are two stack request functions here. <code>requestStackClear()</code> will remove
ALL active states from the state stack. There is a similar function <code>requestStackPop()</code>
which will remove only the topmost state from the stack that would also work here as
there is only one state currently active, but requesting that the stack be cleared
better displays intent. The next line <code>requestStackPush()</code> takes a stateID as a
parameter, so that the <code>StateStack</code> knows which state is being requested of it. If
you're wondering why the function names are prefixed with 'request' this is because
state operations are delayed - they are 'requested' and the <code>StateStack</code> will only
perform these requests at the beginning of the next frame if it is safe to do so.<br />
<br />
With this done the new <code>GameState</code> should be ready to use. Build and run the source and
when the menu is displayed click 'Play'. The menu should disappear and be replaced with
a blank screen. This is because we've not yet added anything to the <code>GameState</code> - but if
you want some confirmation of the state change, edit the constructor of <code>GameState</code> and
add<br />
<br />
<pre><code>xy::Logger::log("Open game state!", xy::Logger::Type::Info);</code></pre>
<pre><code> </code></pre>
Build and run the game again, this time when Play is clicked and the blank screen
appears the "Open game state!" message should appear in the system console. If you don't
have a console window open press F1 to open the xygine console, where the timestamped
message will be displayed.<br />
<br />
<hr />
<h3>
Scene Setup</h3>
Now that the <code>GameState</code> is created we have an arena to finally start implementing the
game. Firstly, to ensure a consistent experience, the camera needs to be fixed to a set
size across all states, and at all window resolutions. xygine uses a default size of
1920x1080 world units, which is automatically applied to a view inside the <code>StateStack</code>
when the window is resized. This way the same view is automatically letterboxed and
mapped to the window. To apply the calculated view on startup, navigate to
<code>Game::initialise()</code> in Game.cpp, and below the line which says<br />
<br />
<pre><code>getRenderWindow()->setKeyRepeatEnabled(false);</code></pre>
<pre><code> </code></pre>
add<br />
<br />
<pre><code>getRenderWindow()->setView(m_stateStack.updateView());</code></pre>
<pre><code> </code></pre>
This line will force the stack to calculate an up-to-date view and immediately apply it
to the RenderWindow when the game is initialised.<br />
<br />
xygine also has a <code>Camera</code> component, used by a <code>Scene</code> to dictate what should be
rendered to the screen. By default this component is initialised to the window size as
it makes no assumptions as for what it shall be used (for instance a <code>Camera</code> may only
show a small area in a <code>Scene</code> used to create a mini-map) so for our two states we'll need
to initialise it to the StateStack's calculated view. Handily this view is passed as one of
the properties of the <code>Context</code> struct which is given to the constructor of every state.
In the constructor of both <code>MyFirstState</code> and <code>GameState</code>, below the call to
<code>createScene()</code>, update the state's <code>Scene</code> with the view stored in the context:<br />
<br />
<pre><code>m_scene.getActiveCamera().getComponent<xy::Camera>().setView(ctx.defaultView.getSize());
m_scene.getActiveCamera().getComponent<xy::Camera>().setViewport(ctx.defaultView.getViewport());
</code></pre>
<br />
adjusting the <code>Scene</code> variable name accordingly for each state.<br />
<h3>
Adding a Paddle</h3>
Now that the camera is set up it's time to start on our paddle entity. Creating the
entity will be very similar to creating the <code>Text</code> entities in <code>MyFirstState</code>, only the
<code>Text</code> component will be replaced with a <code>Sprite</code> component. At the top of the file add
two includes for the <code>Sprite</code> component and the <code>SpriteSystem</code><br />
<br />
<pre><code>xyginext/ecs/components/Sprite.hpp
xyginect/ecs/systems/SpriteSystem.hpp</code></pre>
<pre><code> </code></pre>
The <code>Drawable</code> component and <code>RenderSystem</code> headers should already be there, but if not
add those too<br />
<br />
<pre><code>xyginext/ecs/components/Drawable.hpp
xyginext/ecs/systems/RenderSystem.hpp</code></pre>
<pre><code> </code></pre>
In <code>createScene()</code> an instance of the <code>SpriteSystem</code> needs to be added to the <code>Scene</code>
one line before the <code>RenderSystem</code> is added<br />
<br />
<pre><code>m_gameScene.addSystem<xy::SpriteSystem>(messageBus);</code></pre>
<pre><code> </code></pre>
The rest of the <code>createScene()</code> function follows the same pattern as <code>MyFirstState</code>. As
we're using a <code>Sprite</code> instead of a <code>Text</code>, however, the font from <code>MyFirstState</code> is
replaced with an <code>sf::Texture</code>. Add a new member to the <code>GameState</code> class
<code>sf::Texture m_paddleTexture</code>. This will eventually be replaced with a resource holder,
but for now it will do. Back in the definition of <code>createScene()</code> load the paddle image
into the texture<br />
<br />
<pre><code>m_paddleTexture.loadFromFile("assets/images/paddle.png");</code></pre>
<pre><code> </code></pre>
The image is included in the tutorial repository, but you will need to make sure the
path is set correctly relative to your working directory, depending on your development
environment.<br />
Assuming the texture is now loaded correctly we can create a single entity to act as the
paddle.<br />
<br />
<pre><code>auto entity = m_gameScene.createEntity();
entity.addComponent<xy::Transform>().setPosition(xy::DefaultSceneSize.x / 2.f, xy::DefaultSceneSize.y - 40.f);
entity.addComponent<xy::Sprite>(m_texture);
entity.addComponent<xy::Drawable>();</code></pre>
<pre><code> </code></pre>
This looks very similar to creating a text entity in <code>MyFirstState</code>, with the difference
that the <code>Text</code> component / font combo is replaced with a <code>Sprite</code> component and
texture. The paddle is positioned based on a built-in constant <code>xy::DefaultSceneSize</code>.
This is the same size as the <code>StateStack</code>'s calculated view, so we can be sure that the
paddle will be aligned relative to the bottom centre of the screen as we expect,
regardless of the size or resolution of the window.<br />
To neaten up the layout of the paddle we can take the <code>Sprite</code>'s texture bounds and use it
to align the origin to the centre of the <code>Sprite</code><br />
<br />
<pre><code>auto paddleBounds = entity.getComponent<xy::Sprite>().getTextureBounds();
entity.getComponent<xy::Transform>().setOrigin(paddleBounds.width / 2.f, paddleBounds.height / 2.f);
</code></pre>
<br />
The <code>Sprite</code> function <code>getTextureBounds()</code> differs from <code>getTextureRect()</code> in the fact
that it returns the resulting size of the <code>Sprite</code> based on the current texture bounds,
without the offset coordinates into the texture itself. In other words it is the same
size as the current texture bounds, but the left and top properties are always zero.<br />
<br />
Build and run the tutorial, then click Play on the menu. At the bottom centre of the
screen you'll see our newly created paddle!<br />
<br />
<hr />
<h3>
Player Input</h3>
To make the paddle respond to mouse input we can use xygine's <code>CommandTarget</code> component
with a <code>CommandSystem</code> added to the <code>Scene</code>. The <code>CommandTarget</code> merely holds an integer
ID used to identify which entities should be responding to specific commands. Add a new
header file to the include directory and name it <code>CommandIDs.hpp</code>. Inside create a new
namespace <code>CommandID</code> and add an anonymous enum. This enum will contain all the command
IDs which we'll eventually create - to start with add a member named <code>Paddle</code> and give
it the value 0x1. Each time a new member is added <i>its value should double</i> that of the
previous member. This is so that multiple IDs can be OR'd together as flags. For more
information on this see the <code>CommandTarget</code> <a href="https://github.com/fallahn/xygine/wiki/Commands" target="_blank">documentation on the xygine wiki</a>. When you
have done this include the file at the top of GameState.cpp. If you're using the CMake
files to build the project don't forget to add the CommandIDs.hpp entry to the
CMakeLists.txt in the include directory.<br />
<br />
To assign the ID to our paddle include the header for the <code>CommandTarget</code> component<br />
<br />
<pre><code>xyginext/ecs/components/CommandTarget.hpp</code></pre>
<pre><code> </code></pre>
and add the component to the paddle entity in <code>createScene()</code>. Because <code>addComponent()</code>
returns a reference to the newly created component we can also assign the command ID
immediately<br />
<br />
<pre><code>entity.addComponent<xy::CommandTarget>().ID = CommandID::Paddle;</code></pre>
<pre><code> </code></pre>
To be able to process the commands we're going to send we also need an instance of the
<code>CommandSystem</code> in the <code>Scene</code>. Include the <code>CommandSystem</code> header<br />
<br />
<pre><code>xyginext/ecs/systems/CommandSystem.hpp</code></pre>
<pre><code> </code></pre>
and add it to the <code>Scene</code> before the <code>SpriteSystem</code> in <code>createScene()</code><br />
<br />
<pre><code>m_gameScene.addSystem<xy::CommandSystem>(messageBus);</code></pre>
<pre><code> </code></pre>
Now we're ready to start sending commands!<br />
<br />
The <code>GameState</code> class has a function named <code>handleEvents()</code> in which we can pick up any
mouse movement events passed down from the <code>Game</code> class. To identify events first
include the SFML event header<br />
<br />
<pre><code>SFML/Window/Event.hpp</code></pre>
<pre><code> </code></pre>
before moving to <code>handleEvent()</code>. At the top of the <code>handleEvent()</code> function add<br />
<br />
<pre><code>if(evt.type == sf::Event::MouseMoved)
{
auto worldMousePosition = xy::App::getRenderWindow()->mapPixelToCoords({evt.mouseMove.x, evt.mouseMove.y});
xy::Command cmd;
cmd.targetFlags = CommandID::Paddle;
cmd.action = [worldMousePosition](xy::Entity entity, float)
{
//clamp the X position in the screen area minus the sprite width
float posX = worldMousePosition.x;
auto spriteWidth = entity.getComponent<xy::Sprite>().getTextureBounds().width / 2.f;
posX = std::max(spriteWidth, worldMousePosition.x);
posX = std::min(posX, xy::DefaultSceneSize.x - spriteWidth);
auto& tx = entity.getComponent<xy::Transform>();
auto currentPosition = tx.getPosition();
currentPosition.x = posX;
tx.setPosition(currentPosition);
};
m_gameScene.getSystem<xy::CommandSystem>().sendCommand(cmd);
}</code></pre>
<pre><code> </code></pre>
This is simpler than it looks once we break it down:<br />
First the event type is checked to make sure that it's a <code>MouseMoved</code> event. If it is,
grab the mouse position contained in the event, which is in window coordinates. These
are then converted to world coordinates using the <code>sf::RenderWindow</code> function
<code>mapPixelToCoords()</code>. For more information on what this does see the SFML documentation,
however for our purposes it converts the mouse coordinates into values which are relative
to our <code>Scene</code> coordinates.<br />
With the coordinates in hand we can now create our command<br />
<br />
<pre><code>xy::Command cmd;</code></pre>
<pre><code> </code></pre>
and tell it to target our paddle entity by setting the target flags to the <code>CommandID</code>
we gave the paddle.<br />
<br />
<pre><code>cmd.targetFlags = CommandID::Paddle;</code></pre>
<pre><code> </code></pre>
The important part of the command is the lambda expression assigned to <code>cmd.action</code>. The
signature takes an entity and a float as a parameter. When this command is executed the
target entity, in this case the paddle, is passed in, along with the elapsed or delta
time of the current frame. In this instance the delta time is not used so the parameter
is omitted.<br />
The mouse position is also copy-captured as it is needed to set the position of the
paddle. In the body of the lambda the size of the paddle sprite is obtained and used to
clamp the x position of the mouse coords. This prevents the paddle from leaving the
screen area. Then the current paddle position is read, the x position updated, and the
new position applied.<br />
Once the lambda has been defined the command can be sent.<br />
<br />
<pre><code>m_gameScene.getSystem<xy::CommandSystem>().sendCommand(cmd);</code></pre>
<pre><code> </code></pre>
Here it should be noted that commands are not executed immediately, rather they are
placed in a queue, ready for the next frame. At the beginning of the next frame the
entire queue is parsed and all the commands are executed sequentially. This is important
when creating a lambda expression for the action, should it capture any variables by
reference - the lambda is not executed in the same scope as it is written!!<br />
<br />
If you build and run the tutorial now you'll find that when you move the mouse left or
right the paddle will follow it. As a final touch we can hide the mouse cursor in the
<code>GameState</code>. In the <code>GameState</code> constructor add a line after initialising the camera<br />
<br />
<pre><code>ctx.appInstance.setMouseCursorVisible(false);</code></pre>
<pre><code> </code></pre>
That's it! In the <a href="https://trederia.blogspot.com/2019/09/getting-started-with-xygine-part-3.html">next part</a> of the tutorial we'll be looking at creating custom
components and systems to add a ball entity, and resource management for fonts and textures.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-88105692273996436362019-09-06T19:00:00.000+01:002019-09-06T19:00:00.310+01:00Getting started with xygine - Part 1<h3>
Introduction</h3>
xygine is a small game framework based around SFML that I have developed over the past
few years to create 2D games (examples of which can be found <a href="https://github.com/fallahn/xygine/wiki/Games-made-with-xygine" target="_blank">here</a> and <a href="https://github.com/fallahn/osgc/tree/master/screens" target="_blank">here</a>), which runs on Windows, linux and macOS. It has become
mature enough now, I think, that itās time to attempt to document the process of
creating a small breakout style game, in order to (hopefully) better explain xygineās
features and the functionality it provides. If youāre reading this Iām going to assume
some knowledge on your part of C++ as well as potentially SFML, and that you are here
because you want a shortcut to skip writing much of the boilerplate code required for
creating a game in favour of diving right in. This is not a beginner's tutorial in
gamedev, rather an exploration of what xygine has to offer. Letās begin!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV3wX4FBPpxCwvg1GzPE-QkdbDCGJe5CSuDIKpdJJeWEiJkdSxwUdaq9WgffmWJcz8yOhJoKCijKqBYFHoRg7rc3nsCYIIdOYHmp9WVw9qIbXzNBWmZChPkfJRsLZCafekwGPGyM8SOio/s1600/samples.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="540" data-original-width="1440" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV3wX4FBPpxCwvg1GzPE-QkdbDCGJe5CSuDIKpdJJeWEiJkdSxwUdaq9WgffmWJcz8yOhJoKCijKqBYFHoRg7rc3nsCYIIdOYHmp9WVw9qIbXzNBWmZChPkfJRsLZCafekwGPGyM8SOio/s640/samples.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<h3>
Getting started</h3>
xygine is a fully open-source project, licensed under the zlib license. The best way to
get started is to clone the repository available on github, and build it yourself. The
repository can be found at <a href="https://github.com/fallahn/xygine">https://github.com/fallahn/xygine</a> and the build instructions
are on the wiki: <a href="https://github.com/fallahn/xygine/wiki/Building">https://github.com/fallahn/xygine/wiki/Building</a> <br />
Once you have configured, built and installed xygine for your platform of choice, itās
time to create your first project! The repository contains a folder named
<code>cmake_template</code>. This folder has all the files necessary to start a new project,
including a default CMake file. Create a copy of this directory and rename it to
something else such as ābreakoutā. If you want to use the included CMake file then open
that in your text editor of choice and edit the PROJECT_NAME variable to something more
relevant too. If, like me, you prefer using an IDE you can open the CMake file directly
if your IDE has support (such as Visual Studio 2017) or create a new project, adding
the source and header files included in the template folder, and linking to the SFML
and xygine libraries as appropriate (eg the correct debug/release versions). If you are
taking the CMake route then the src and include subdirectories both contain a
CMakeLists.txt file, which will need to be updated when you add any new source files to
your project. Once you have the project configured it should be possible to build and
run it, which will open a console window (on Windows) and a blank game window titled
āxyginext gameā. Pressing F1 will open a console window which displays any messages
printed with the <code>xy::Logger</code> class, and provides basic video and audio options. More
information on the console can be found in the documentation:
<a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a> <br />
<h3>
A closer look at the source files</h3>
The template project contains three sources files. <br />
Main.cpp contains the entry point into the program, which instantiates a <code>Game</code> object
and runs it. Thereās not much else done here and the file will probably not be brought
up again for the rest of the tutorial.<br />
<br />
Game.cpp is where the magic begins to happen. The <code>Game</code> class inherits the <code>xy::App</code>
class which is the root of any xygine game or application. The <code>App</code> class contains the
render window instance, as well as manages events and system messages, making sure that
they are dispatched correctly to the rest of the game. Documentation on <code>App</code> specific
features (such as the message bus) can be found on the xygine wiki
<a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a> . Because the <code>Game</code> class inherits <code>App</code> it has
access to these features through the <code>App</code> interface. The <code>Game</code> class is home to the
<code>StateStack</code> - which will be covered later - and makes sure that events, messages and
updates are correctly forwarded, as well as ensuring the active states are rendered.
Most important to note about the <code>Game</code> class, however, are the <code>initialise()</code> and
<code>finalise()</code> functions, which are overrides of the <code>App</code> class functions. These are used
to correctly create, configure and tidy up any extra game-wide resources which you may
use throughout the lifetime of the application. They are called automatically on startup
and shutdown and should be used if a game uses any third party libraries such as a
physics system which requires specific initialisation and shutdown. The initialise
function returns a bool - this should return false if any initialisation errors occur,
particularly if initialising a third party library, as this will cause xygine to quit
safely, rather than try to continue.<br />
<br />
MyFirstState.cpp implements a simple <code>State</code>. States are used with xygineās <code>StateStack</code>
class, which is optional but recommended. The <code>StateStack</code> is a concept which allows
several states to be active at any one time, and controls how events and updates are
passed down through the active states. A <code>State</code> object encapsulates a particular state
of the game, such as the main menu, a gameplay state, or a pause menu. The advantage of
a <code>StateStack</code> is the ability to keep existing states alive, such as a game state, while
placing a another state on top. States are rendered from the bottom of the stack
upwards, and updated from the top down. This means that a game state at the bottom will
be drawn first, with a menu (for example) in a pause state drawn on top. Conversely the
pause state will receive updates and events such as keyboard and mouse first, and can
āconsumeā these events by returning false from the corresponding state functions:
<code>handleEvent()</code> and <code>update()</code>. This means that the menu remains responsive and active,
while preventing the lower game state from being updated, effectively pausing it.
Popping the the pause state from the top of the stack then allows the game state below
to continue to execute as it starts to receive events and updates again. When used
correctly this can be a very powerful and useful concept. For a more detailed look at
the implementation of a <code>StateStack</code> I suggest reading āSFML Game Developmentā by
Jan Haller, Henrik Vogelius Hansson and Artur Moreira - a book which was very
influential when I started creating xygine.
<code>MyFirstState</code> itself implements the interface of <code>xy::State</code>.
The functions are similar to that of the <code>Game</code> class - <code>handleEvent()</code> which passes
SFML events such as keyboard and mouse input down to any game classes which need them,
<code>handleMessage()</code> used to forward system messages to interested parties, <code>update()</code>
which processes delta-time critical logic functions, and <code>draw()</code>, used to render the
state to the screen. The state also returns an identifier via <code>stateID()</code> which
is used when registering the state with the <code>StateStack</code> in the <code>Game::initialise()</code>
function. The specifics of this are covered later. Usually when creating a new project
it is preferable to rename the <code>MyFirstState</code> class to something more coherent such as
<code>MenuState</code> - but for this particular case I will leave it as it is.<br />
<h3>
Setting the Scene</h3>
<code>MyFirstState</code> contains an instance of a <code>xy::Scene</code> object. Scenes are used to
encapsulate the Entity Component System or ECS of xygine, and multiple scenes can exist
within a single state - for example one might be used to create the game area, and
another to display the user interface. Entities are effectively no more than an integer
ID, used to index arrays of components which are attached to them. As such they are very
lightweight and easy to copy around, while still providing access to their attached
components. Components are nothing more than data containers, usually structs, although
some provide a selection of functions to create a cleaner interface, such as
<code>xy::Sprite</code>. The key point is, however, that there is no logic held within a component,
this is all performed by a series of Systems. A system class will look at all the active
entities in a Scene and process each one based on the data held within its components.
xygine already has several component and system classes for often used tasks, but the
ECS really shines when, later on, you create your own custom systems and their
corresponding components. To start with weāll use some of the existing components and
systems to create a basic menu in <code>MyFirstState</code>. At the top of the cpp file include<br />
<br />
<pre><code>xyginext/ecs/components/Transform.hpp
xyginext/ecs/components/Text.hpp
xyginext/ecs/components/Drawable.hpp
xyginext/ecs/systems/TextSystem.hpp
xyginext/ecs/systems/RenderSystem.hpp</code></pre>
<pre><code> </code></pre>
A brief explanation: <br />
The transform component is used by almost all entities to correctly place them in the
<code>Scene</code>. There are very few cases where an entity will not need a transform - text and
sprites cannot be drawn without one, buttons and UI components will not work, and audio
emitters cannot be correctly placed. Transforms can also be parented to one another to
form a āscene graphā. More information can be found about this in the wiki
<a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a> <br />
Text components are very similar to the <code>Text</code> class in SFML, although slightly modified
to better fit the ECS. The interface is nearly the same, including functions such as
<code>setString()</code>, <code>setFillColour()</code> etc, and require an instance of <code>sf::Font</code> to render. For
now a font can be added as a member to <code>MyFirstScene</code> as <code>m_font</code> for example - later this
will be replaced with a resource manager.<br />
<br />
Drawable components are required for any entities which are rendered to the screen. They
contain an <code>sf::VertexArray</code> which, in this case, will be updated by the <code>TextSystem</code>
with data held in the <code>Text</code> component. Sprites and the <code>SpriteSystem</code> work in a similar way.
Later, when creating custom systems, the <code>Drawable</code> component can be used to draw
arbitrary shapes, having their vertex data updated via the custom system. Drawable
components also have texture and shader properties, which allow SFML resources
<code>sf::Texture</code> and <code>sf::Shader</code> to be applied to them. This is not covered here, but
more information can be found in the documentation. Drawables have another
important feature: the depth property. The <code>setDepth()</code> and <code>getDepth()</code> accessors can
be used to define the āz depthā of a drawable, providing runtime sorting of visible
drawables. Setting a depth with a higher value ensures that the drawable is drawn in
front of those with a lower value. Itās worth noting that there is no guarantee of order
with drawables that have the same value as another - this might cause some visible
flickering so in these cases a specific value should be set. The default value is 0.
Finally the <code>RenderSystem</code> is used by the <code>Scene</code> to sort and cull any visible entities
which have a <code>Drawable</code> component, and render them to the screen.<br />
<h3>
Setting up the ECS</h3>
With the correct files included, the <code>Scene</code> can now be initialised. For convenience
create a private member function in <code>MyFirstState</code> called <code>createScene()</code>, and call it
from the <code>MyFirstState</code> constructor. Then define <code>createScene()</code><br />
<br />
<pre><code>void MyFirstState::createScene()
{
//add the systems
auto& messageBus = getContext().appInstance.getMessageBus();
m_scene.addSystem<xy::TextSystem>(messageBus);
m_scene.addSystem<xy::RenderSystem>(messageBus);
//load resources
m_font.loadFromFile(āassets/fonts/my_lovely_font.ttfā);
//create an entity
auto entity = m_scene.createEntity();
entity.addComponent<xy::Transform>().setPosition(200.f, 200.f);
entity.addComponent<xy::Text>(m_font).setString(āHello world!ā);
entity.addComponent<xy::Drawable>();
}</code></pre>
<pre><code> </code></pre>
So what have we just done? Firstly weāve added the necessary systems to the scene. All
systems require a reference to the xygine message bus so that they may subscribe to it.
This is retrieved through the <code>getContext()</code> function, which is useful for accessing
game/application properties such as the render window or message bus.
<code>xy::Scene::addSystem()</code> is a templated function which takes the system type as a
template parameter. The function parameters are all forwarded to the constructor of the
system itself - multiple parameters can be passed, which is useful when creating custom
systems. The default xygine systems, however, take only a reference to the message bus.
Systems are updated and drawn in the order in which they are added to the scene, which
is sometimes important. In this case we want the <code>Text</code> components to be updated before
the <code>RenderSystem</code> draws them.<br />
Next the font is loaded, which was added as a member to <code>MyFirstState</code> as <code>m_font</code>. This
will eventually be replaced with a resource manager. Note that when loading the font
there are no defaults supplied with xygine - you need to provide a path to your own. If
the text is not visible then this is probably the first thing to check.<br />
<br />
Finally a new entity is created. Entities are created through a <code>Scene</code>ās factory function
<code>createEntity()</code>. This is important because it ensures that the entity is correctly
registered to its parent scene. The entity class is merely a handle, however, and instances
can happily be overwritten when creating new entities. For example:<br />
<br />
<pre><code>auto entity = m_scene.createEntity();
//add components to entity
entity = m_scene.createEntity();
//add other components to new entity</code></pre>
<pre><code> </code></pre>
All entities will continue to live within a scene until explicitly destroyed, which will
mark them for garbage collection:<br />
<br />
<pre><code>m_scene.destroyEntity(entity);</code></pre>
<pre><code> </code></pre>
This generally means that entities will exist until the end of the current frame.<br />
NOTE: xygine has a hard limit of 1024 active entities at a time. While this can be changed
by modifying the source code it is usually enough as long as expired entities are explicitly
destroyed. There is very little overhead in removing and creating entities at runtime because
xygine automatically pools and reallocates the memory required.<br />
<br />
Components are added to an entity with <code>addComponent<T>()</code>. Similarly to the <code>Scene</code>ās
<code>addSystem()</code> function, the entityās <code>addComponent()</code> function is templated, taking the
component type as a type parameter. The function also forwards any parameters to the
component constructor, such as in this example where a reference to the font is
forwarded to the constructor of the <code>Text</code> component. <code>Entity::addComponent()</code> will return a reference to the
newly created component allowing properties to be immediately set, for example the
string used in the <code>Text</code> component. A reference to a component can also be retrieved
with <code>entity.getComponent<T>()</code>, allowing other component properties to be updated, or
components to be copied elsewhere. Note that not all components are copyable, for
example the <code>Transform</code> component is only moveable.<br />
Building and running the project should now display the window as before, only with the
words āHello World!ā in the top corner.<br />
<br />
From here I encourage you to experiment with the entities and components - update
the <code>createScene()</code> function to create 3 entities, each with a different text strings:
A title, one which says āPlayā and another which says āQuitā. Try updating different
component properties such as the scale, position or rotation of the <code>Transform</code>
component, or character size or fill colour of the <code>Text</code> component. When you have a
basic menu laid out, move on to the next section.<br />
<br />
<hr />
<h3>
Adding interactivity</h3>
To make the menu usable ideally the entities rendering the text will need to act as
buttons when clicked on. xygine includes a <code>UIHitBox</code> component and a <code>UISystem</code> for
this purpose. At the top of the MyFirstState.cpp include<br />
<br />
<pre><code>xyginext/ecs/components/UIHitBox.hpp
xyginext/ecs/systems/UISystem.hpp</code></pre>
<pre><code> </code></pre>
Now in <code>createScene()</code> add a <code>UISystem</code> to the scene, right before adding the
<code>RenderSystem</code><br />
<br />
<pre><code>m_scene.addSystem<xy::UISystem>(messageBus);</code></pre>
<pre><code> </code></pre>
UISystems are slightly different from other systems - they require event input from the
state, so that they know when to react to keyboard or mouse input. In
<code>MyFirstState::handleEvent()</code> add the line<br />
<br />
<pre><code>m_scene.getSystem<xy::UISystem>().handleEvent(evt);</code></pre>
<pre><code> </code></pre>
With this set up itās time to make the quit button do something. Hopefully in
<code>createScene()</code> you should now have something like<br />
<br />
<pre><code>entity = m_scene.createEntity();
entity.addComponent<xy::Transform>();
entity.addComponent<xy::Text>(m_font).setString(āQuitā);
entity.addComponent<xy::Drawable>();</code></pre>
<pre><code> </code></pre>
To add interactivity to this entity add a <code>UIHitBox</code> component. This is used to define
an area in the scene in which mouse events, such as button presses, perform a callback.
The size of the text can be retrieved with the static function
<code>xy::Text::getLocalBounds()</code>.<br />
<br />
<pre><code>entity.addComponent<xy::UIHitBox>().area = xy::Text::getLocalBounds(entity);</code></pre>
<pre><code> </code></pre>
Now that the entity has an area defined for it, it needs to know what to do when it
receives an event within the given area. These are done by callbacks registered with the
<code>UISystem</code>. Callbacks are registered with a lambda expression or functor passed to
<code>UISystem::addMouseButtonCallback()</code> which returns an integer ID. The ID is useful
because it means a single callback function can be assigned via its ID to multiple
components, should those components wish to perform the same thing.<br />
<br />
<pre><code>auto callbackID =
m_scene.getSystem<xy::UISystem>().addMouseButtonCallback(
[&](xy::Entity e, sf::Uint64 flags)
{
if(flags & xy::UISystem::LeftMouse)
{
getContext().appInstance.quit();
}
});</code></pre>
<pre><code> </code></pre>
This looks a little wordy, so letās break it down. A reference to the <code>UISystem</code> can be
retrieved from the scene with <code>getSystem<T>()</code>. Then a lambda expression is passed to
<code>addMouseButtonCallback()</code>. The lambda signature for a mouse button callback passes in a
copy of the entity which is executing the callback - that is, the entity which has the
<code>UIHitBox</code> component attached to it, and a copy of the event flags. Event flags can be
used to check which mouse button was pressed. In this case weāre looking for the left
mouse button, and if it is true the game is quit. (<code>xy::App::quit()</code> is the preferred
method, as it makes sure everything is finalised properly before closing the game).
Other available callbacks, their signatures and their flags are detailed in the
documentation. <a href="https://github.com/fallahn/xygine/wiki">https://github.com/fallahn/xygine/wiki</a><br />
<br />
The result of registering the callback is returned as <code>callbackID</code>. To associate this
with our hitbox (and potentially any others) it needs to be added to the hitbox callback
array.<br />
<br />
<pre><code>entity.getComponent<xy::UIHitBox>().callbacks[xy::UIHitBox::MouseUp] = callbackID;
</code></pre>
<br />
Now whenever the hitbox detects a mouse button up event the callback with callbackID is
performed. In this case the button is checked to see if it is the left button, and if it
is the game is quit.<br />
<br />
Compile and run the project. Clicking on the text which says āQuitā should now close the
window.<br />
From here, using the documentation as reference, experiment with the <code>UISystem</code> and its
callbacks. As well as mouse events try handling keyboard events. Mouse move events can
be used to trigger callbacks which highlight the selected text for example.<br />
<br />
The source for this tutorial can be found <a href="https://github.com/fallahn/xygine/tree/master/tutorial" target="_blank">here</a>. Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-7122225579411542512018-09-13T19:00:00.000+01:002018-09-13T19:00:03.059+01:00Building a custom MIDI controller part 3 - Tweaks and refinementsIn this final post about my custom MIDI controller (the first two posts can be found <a href="https://trederia.blogspot.com/2018/08/building-custom-midi-controller-part-1.html" target="_blank">here</a> and <a href="https://trederia.blogspot.com/2018/09/building-custom-midi-controller-part-2.html" target="_blank">here</a>) I'm going to cover what happened when I tested the unit for the first time, the problems I uncovered, and how I worked around them.<br />
<br />
At this point the project wasn't fully assembled, although it was all connected. Experience has taught me to expect a few kinks, and I anticipated the need for a few tweaks to the software. Fortunately I only encountered a couple of problems.<br />
<br />
<b>The noise problem.</b><br />
In the first post I briefly discussed the flow of the program, and the fact that I was using <a href="http://www.midiox.com/" target="_blank">MIDI OX</a> to analyse MIDI data traveling back and forth between the computer and the Arduino. It was immediately obvious something was wrong as soon as I opened the MIDI OX window because the display was deluged with data from the analogue inputs. When I described the program flow I explained I was reading the analogue inputs in a loop, and then sending MIDI data out if there was any change since the last time the input was read. As it turned out there was some amount of noise - a very small amount - but enough to cause the input value to fluctuate and send out a stream of data. I first considered a hardware fix using a resistor and a small capacitor to create a low-pass filter on the inputs, but before I pulled apart the nest of wiring I thought I'd first check if there was a software solution.<br />
<br />
I'm very glad I did because I quickly came upon <a href="https://www.norwegiancreations.com/2015/10/tutorial-potentiometers-with-arduino-and-filtering/" target="_blank">this article</a> that covered noise filtering of potentiometer inputs with Arduinos (and AVRs in general). With a single line of code I could implement an exponential moving average low pass filter, which smoothed out the jitter I was experiencing.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> EMA_S = (EMA_a*sensorValue) + ((1-EMA_a)*EMA_S);</span><br />
<br />
Suddenly everything was quiet in the MIDI OX console, and problem number one was solved.<br />
<br />
<b>The feedback problem.</b><br />
My elation didn't last that long, however, because as soon as I pressed one of the buttons the MIDI OX console went bananas again. This had me scratching my head for a little while, but I had noticed that the console output apparently displayed far more regular values than the previous noisy ones. Eventually I twigged that I had the MIDI Thru option enabled in the software library, causing any MIDI output to be re-routed via MIDI OX right back into the Arduino - which then re-broadcast it via MIDI Thru and so on... effectively a MIDI feedback loop. A quick look at the <a href="https://github.com/FortySevenEffects/arduino_midi_library/wiki" target="_blank">library documentation</a> was all that was needed to find out how to disable the behaviour. Excellent.<br />
<br />
<b>The practical problem.</b><br />
As it happens not everything goes wrong because of my careless programming - sometimes it's down to my lazy wiring. With most of the controls now working and outputting values to the MIDI OX console as I expected, I found I would get maybe a rogue knob or two which would intermittently send values for a different control. Sometimes the control would output values up and down the scale without me even touching it. It seemed most irregular and looking again and again at the source code I couldn't see anything obviously wrong. It was while I was unplugging the ISP programmer (again) I actually noticed that leaning on a junction of wires altered the output of MIDI OX - and squeezing it appeared to stop the problem completely. I realised here that I actually still had a bodged connector created from header pins and hot glue during the prototyping stage which I hadn't replaced.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUqSaIwIgyzzJ_TNqmcry9-_vL_790K3cnNV-y-JCj-ZHQ0-bq1qkjumUNNJgMYPoRq_vFffc7TulUvnpBHJ0a56mO3LONocRlwUoMoQyXeuoVC6Of8Crkal-R-2Bi94dBOvWuWuXAPZY/s1600/bodge.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="426" data-original-width="717" height="380" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUqSaIwIgyzzJ_TNqmcry9-_vL_790K3cnNV-y-JCj-ZHQ0-bq1qkjumUNNJgMYPoRq_vFffc7TulUvnpBHJ0a56mO3LONocRlwUoMoQyXeuoVC6Of8Crkal-R-2Bi94dBOvWuWuXAPZY/s640/bodge.jpg" width="640" /></a></div>
<br />
Looking at the fritzing diagram in the <a href="http://trederia.blogspot.com/2018/08/building-custom-midi-controller-part-1.html" target="_blank">first post</a>, these wires are represented by the yellow lines, which connect the channel selection of the multiplexers. Well that would explain why the knobs were interfering with each other. Twenty minutes later I'd crafted, soldered and heat-shrunk a proper loom to replace the bodge job, and with that the final problem was gone.<br />
<br />
<b>Conclusion.</b><br />
This was a fun and challenging project which took about 2 weeks to complete, and taught me a lot about the more practical side of electronics, including not to glue things to the work bench and to replace any bodge connections with proper wiring. Unfortunately it seemed I had to learn <i>yet again</i> that my laziness would sooner or later bite me from behind. For future endeavours I also learned to account for how much room the wiring will take up - not just the room required for the components themselves. I'm very pleased with the result and there's not much I would change if I were to do it again. If anything I'm looking to expand on it by adding more controls sometime in the future. For now though I'll end this series with a picture of the completed controller, and a video of it in action.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiihTwKNR-BtF0O2Hq-e10aGAmIxmQBGn8JOUYw_fIjFDhi03wLz3CMKu0N4SvJne_fkSs8DPSfPtRGDmwH6MDpGHyJkumpYqn8mLB4YjwVUfGSarmOe8Qj25lKhmG9cdLNbYK4pIynfX8/s1600/finished.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="729" data-original-width="884" height="526" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiihTwKNR-BtF0O2Hq-e10aGAmIxmQBGn8JOUYw_fIjFDhi03wLz3CMKu0N4SvJne_fkSs8DPSfPtRGDmwH6MDpGHyJkumpYqn8mLB4YjwVUfGSarmOe8Qj25lKhmG9cdLNbYK4pIynfX8/s640/finished.jpg" width="640" /></a></div>
<br />
<br />
<iframe allow="autoplay; encrypted-media" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/roekoRrj8jQ?rel=0" width="560"></iframe>
<br />
<a href="https://github.com/fallahn/midicontroller" target="_blank">Project repository on Github</a>Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-81005857021129976932018-09-06T19:00:00.000+01:002018-10-07T16:02:46.253+01:00Building a custom MIDI controller part 2 - Case Design<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
In my <a href="https://trederia.blogspot.com/2018/08/building-custom-midi-controller-part-1.html" target="_blank">last post</a> I outlined the goal of the MIDI controller project, and detailed the design behind the electronics part. If you haven't read it yet check it out <a href="https://trederia.blogspot.com/2018/08/building-custom-midi-controller-part-1.html" target="_blank">here</a>, as this post carries on from where it left off.<br />
<br />
I should point out that this build was afforded to me by the company I work for, who sponsored it in time, equipment use and design software. Please check out what they do by going to <a href="http://www.ktassemblies.co.uk/">www.ktassemblies.co.uk</a> if this post is of any interest to you.<br />
<br />
<b>Conception</b><br />
The first thing that I decided was that the controls should be mounted on some kind of board, which could be suspended over a box containing the main circuit - in this case consisting of the Arduino and two multiplexer boards. Ideally I wanted a custom box, but as I don't have access to anything like a 3D printer my options were to craft something another way, or buy a prefabbed project box from somewhere like RS components. Such boxes are usually very dull and often grey or beige, and rarely the most economic spatially - they are either far too large, or impractically small. Resorting then to creating my own enclosure I looked at which tools I had available and started to consider how to work with them. With a desktop laser cutter and a CNC router at my disposal the obvious material options were wooden board (either ply or MDF) and acrylic sheet. I settled on ply for the top and bottom parts of the enclosure as this could be worked with the CNC router to provide accurate mounting holes, as well as precision recesses for the button and slider fixings used to mount the sliders. <a href="https://www.3ds.com/products-services/draftsight-cad-software/free-download/" target="_blank">Draftsight</a>, which has a free version, provided a means to quickly sketch out a layout as well as make sure all the components fitted comfortably together with minimal wasted space.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj7ubuDYNn8Nt3MaxIz50296SwTLhHi6EMdIWat3b8RO9y5Q5CgOuNmUlOBuz0kkbtNLSvD5VAP1ZtqHBsQMQGKpIjcVoTgWTfqBbfmI4WWJQ8-_izRyRgbp-6snmlpbgHt4bHQ4GIfEA/s1600/mixer_plate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="678" data-original-width="1600" height="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj7ubuDYNn8Nt3MaxIz50296SwTLhHi6EMdIWat3b8RO9y5Q5CgOuNmUlOBuz0kkbtNLSvD5VAP1ZtqHBsQMQGKpIjcVoTgWTfqBbfmI4WWJQ8-_izRyRgbp-6snmlpbgHt4bHQ4GIfEA/s640/mixer_plate.png" width="640" /></a></div>
I overlaid the top and bottom plates to ensure there was enough room for all the components.<br />
Ply wood has a nice grain pattern so, in combination with some wood stain, I felt it would also look nicer than MDF. Being wood it also had the advantage that I could easily screw components to it, such as the sliders or circuit boards.<br />
<br />
It was then only a matter of exporting the dxf to the routing software, then cutting the ply on the CNC machine.<br />
<br />
<iframe allow="autoplay; encrypted-media" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/STjXI_efsW4?rel=0" width="560"></iframe>
<br />
To fix the top and bottom together I decided long M4 bolts could be used with some 5mm diameter brass tubing cut to length to act as spacers. I hoped the wood and brass combination would help give it an interesting and slightly retro look. The bolt and spacer method would also work to hold the sides in place by placing them either side of the acrylic sheet I planned to use, weaving the plastic between the spacers. Plastic nut covers on the end of the bolts also doubled as great feet!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjORfnmACjh8pW7X7CwnA8u0y8aWCUC3IfRAC-rvwTDcdfFvOsg6rj-sd2RH3LfypgiyHq_tIwwoiQPrrlBUtgGY9Gg8j1z0GW2cXqTA4m24W020mlIDsR_wDzEZ7q8b23-7Uu7TGZTsQ/s1600/feet.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjORfnmACjh8pW7X7CwnA8u0y8aWCUC3IfRAC-rvwTDcdfFvOsg6rj-sd2RH3LfypgiyHq_tIwwoiQPrrlBUtgGY9Gg8j1z0GW2cXqTA4m24W020mlIDsR_wDzEZ7q8b23-7Uu7TGZTsQ/s640/feet.jpg" width="640" /></a></div>
<br />
<br />
The sides I decided would look good cut from gloss black acrylic sheet, 3mm thick. I could use the laser cutter for this, creating a finger joint cut for the corners, and bond it with acrylic adhesive. I also took this opportunity to cut a pattern in the sides which made it look a bit more interesting, as well as revealing some of the insides to anyone inspecting closely. It was at this point I made a mistake - and managed to glue one of the joints to the workbench! It came away after some teasing but left a rather unfortunate mark.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJdB7Y9v2g6G2D9SW8f-zL87cKOYXafbuG6vjaHa0NgSSrHwY5bONrJjAdX1MrtY0XSNBdXZIBqQ161KVOT6_3ImXMs3Z_uT2-18WJ7L0d3lLobKZqNZYWY8ZvpnpzPbeN52i5hUz3UOg/s1600/before.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJdB7Y9v2g6G2D9SW8f-zL87cKOYXafbuG6vjaHa0NgSSrHwY5bONrJjAdX1MrtY0XSNBdXZIBqQ161KVOT6_3ImXMs3Z_uT2-18WJ7L0d3lLobKZqNZYWY8ZvpnpzPbeN52i5hUz3UOg/s640/before.jpg" width="640" /></a></div>
<br />
Rather than remake the sides, I opted for some brass angle which I placed on the corner:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfxhaeSekrv3VnuoUp4YYrFpyEr_rBBsdCJnE78bbYREzyqkIDu4aIMLlEPc_X_N86PpmFPMIzreg4ZW-TL04zzVX0JoGe6IIcBsN9H3nxUzIJOVGz0LVt5jsCfbiRUTYW_qbjavo01D4/s1600/after.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfxhaeSekrv3VnuoUp4YYrFpyEr_rBBsdCJnE78bbYREzyqkIDu4aIMLlEPc_X_N86PpmFPMIzreg4ZW-TL04zzVX0JoGe6IIcBsN9H3nxUzIJOVGz0LVt5jsCfbiRUTYW_qbjavo01D4/s640/after.jpg" width="640" /></a></div>
<br />
Although I'm still undecided whether or not I prefer it.<br />
<br />
<b>Assembly.</b><br />
With the parts cut and stained it was time to set about first mounting the controls on the top panel, before wiring them together. The previous post explains how the wires are connected so I won't reiterate - but as I added more wires and everything started to come together another problem was becoming apparent...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3p_2F8ZRBxiRorQ-jVoN5RRPIMxt-aXwVPrE6fttDOBp8o9NybfZN3Hy2hWtjwEAUAZrcafleeojGuZPQKEr07cha1hNjc5RkgJvF0wVDioZEWwLN3PyxArse01s8-zRGPcO4tvVUSSA/s1600/01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3p_2F8ZRBxiRorQ-jVoN5RRPIMxt-aXwVPrE6fttDOBp8o9NybfZN3Hy2hWtjwEAUAZrcafleeojGuZPQKEr07cha1hNjc5RkgJvF0wVDioZEWwLN3PyxArse01s8-zRGPcO4tvVUSSA/s640/01.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4usiVkqhLohz7a8ny8mXuyZvCFUwORmDZltyCnxTPC0SkOnuj6DH1TiqpStJGF7rYJH7v85GCKMTz7HCvtkxKLc11dPWXQdvwY1i3rWlV1yGtDznDiRofyS1nskJ8jIwtii1KWdeLqQo/s1600/02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4usiVkqhLohz7a8ny8mXuyZvCFUwORmDZltyCnxTPC0SkOnuj6DH1TiqpStJGF7rYJH7v85GCKMTz7HCvtkxKLc11dPWXQdvwY1i3rWlV1yGtDznDiRofyS1nskJ8jIwtii1KWdeLqQo/s640/02.jpg" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDJ2bL30JUd9LQzFiG3fmMY7nyBqEmCEOdhdPea2rOjOgVSbwDfrfxo21CI138oHaO1ND19BuQse0q-MkSr3w1MAucSS7AXsb6E3IX-kRRgZ7rkTdPjEEafNPXlUta_HKh9lDG3_VjvJg/s1600/03.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDJ2bL30JUd9LQzFiG3fmMY7nyBqEmCEOdhdPea2rOjOgVSbwDfrfxo21CI138oHaO1ND19BuQse0q-MkSr3w1MAucSS7AXsb6E3IX-kRRgZ7rkTdPjEEafNPXlUta_HKh9lDG3_VjvJg/s640/03.jpg" width="640" /></a></div>
<br />
I had failed to take into account just how much space the wires were going to take up inside the enclosure. To be honest I'm still not entirely sure how I would have done so during the design stage - but I briefly worried that I was going to have to remake the sides after all, as well as re-cut the brass tubing to make the entire build deeper. Fortunately everything just about fitted, but it's certainly worth noting for any future projects...<br />
<br />
<b>Finishing touches.</b><br />
With everything together the only remaining piece was the top of the panel, which looked pretty rough with the fader mounting plates exposed. This was remedied with a laser engraved plastic laminate, which I chose in black to match the sides. Not only did it give a smooth finish it provided the opportunity to mark on some labels as well as some basic graphic design. I probably could have added a bit more detail here, perhaps some patterning to match the sides - but as the laminate is removable I can (and may well) replace it with something a bit fancier.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTIEEiSBviNjJdaCqVui0XhgAzlPcapR-RM7lPPJ7XxELMUhfaximx-fEOV7JfncMcUTDjVhAXSfNa4o1TaNd2kpk8RMdgjwSwkxUBKuw7IZkPsImHlh3az5erBjeVLekjORLs3vqK6Zo/s1600/without.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTIEEiSBviNjJdaCqVui0XhgAzlPcapR-RM7lPPJ7XxELMUhfaximx-fEOV7JfncMcUTDjVhAXSfNa4o1TaNd2kpk8RMdgjwSwkxUBKuw7IZkPsImHlh3az5erBjeVLekjORLs3vqK6Zo/s640/without.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpEejpAaTYqPH9y3NrrioJLSkig4KRBCvWdyDtqLvxQ4w80fVVvYFl8aXaijCM_FSN5dROCFw_8bcgLR4c9sZ4FbJOq5qh7clrjlowikblRYlW_DvTF-e9QlZqzflx9Q12qMzXchnPjbc/s1600/with_light.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpEejpAaTYqPH9y3NrrioJLSkig4KRBCvWdyDtqLvxQ4w80fVVvYFl8aXaijCM_FSN5dROCFw_8bcgLR4c9sZ4FbJOq5qh7clrjlowikblRYlW_DvTF-e9QlZqzflx9Q12qMzXchnPjbc/s640/with_light.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIkKZrMheJIdR6SMjWW-Zj0_jbSga8k6tyUefkOyTFIzEdor4YP8qpN-uvyht3FhonjDqEYbLSEhYXuucjdfEKeHVRSIVgDIE3qgxc4U5syNIkqvlEgVqnfiw2fPEWwKdsgZGAZ9cB8fo/s1600/with_dark.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1296" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIkKZrMheJIdR6SMjWW-Zj0_jbSga8k6tyUefkOyTFIzEdor4YP8qpN-uvyht3FhonjDqEYbLSEhYXuucjdfEKeHVRSIVgDIE3qgxc4U5syNIkqvlEgVqnfiw2fPEWwKdsgZGAZ9cB8fo/s640/with_dark.jpg" width="640" /></a></div>
<br />
So now it had the looks, it had the wires, and it had the electronics. It was time to actually test the controller, which I cover in the next post <a href="https://trederia.blogspot.com/2018/09/building-custom-midi-controller-part-3.html" target="_blank">here</a>.<br />
<br />
<a href="https://github.com/fallahn/midicontroller" target="_blank">Project repository on Github</a> Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-67619391442149059382018-08-30T19:00:00.000+01:002018-10-07T16:01:46.179+01:00Building a custom MIDI controller part 1 - PrototypingI've been interested in DJing and music production since my early teens, ever since I met a friend's dad who had a MIDI powered studio in his home which included a software sequencer running on an Atari ST. I started to collect vinyl when I was about 14, and although I haven't bought much in the last 10 years or so I've always had a soft spot for collecting music. Occasionally I will indulge myself by buying a collection of mp3s from my favourite online retailer, and use <a href="https://www.native-instruments.com/en/products/traktor/dj-software/traktor-pro-2/" target="_blank">Traktor Scratch</a> along with a <a href="https://www.numark.com/product/mixtrack-pro-ii" target="_blank">Numark Mixtrack Pro 2</a> to record myself something to listen to while I code away my days. During the most recent of these sessions I found myself pondering just how the Numark MIDI controller worked, and with a little bit of research realised that it wouldn't be too hard to build myself my own custom controller. Don't get me wrong, the Mixtrack Pro is a capable piece of kit - even if it's not the Pioneer knobs the cool kids are twiddling today - but there's a certain satisfaction in building and using your own equipment.<br />
<br />
So what do I mean when I say 'MIDI controller'? If you came here through a search engine then chances are you already know, but for those who don't here is a brief run down: <a href="https://en.wikipedia.org/wiki/MIDI" target="_blank">MIDI</a> is a serial data protocol used to transmit note, velocity and other data between pieces of electronic equipment, originally designed so that a single MIDI keyboard could be used to control multiple synthesisers or sound generators, or so that a sequencer (either in software or hardware) could transmit pre-arranged note data to multiple pieces of equipment. In this context my MIDI controller is used to send commands to the mixer faders, volume and EQ controls and special effects parameters of the Traktor DJ software. It is a digital representation, if you will, of the traditional turntable setup, with a hardware interface sending data, via MIDI, to the computer. It looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWgL0jtRDEiQ7M_8jUwSclNX1utAgiv9jx-UBYjmA7iJKAwJOREujA6O7JWk4E1p0Uhpo2WNSbYXuZd8L1E0drk9hPxZdCrDfNCH9JhlTbh-_igIZgF-K7V8K3km2uK7p6-R_GxGuA13o/s1600/IMG_20180828_183804814.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="648" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWgL0jtRDEiQ7M_8jUwSclNX1utAgiv9jx-UBYjmA7iJKAwJOREujA6O7JWk4E1p0Uhpo2WNSbYXuZd8L1E0drk9hPxZdCrDfNCH9JhlTbh-_igIZgF-K7V8K3km2uK7p6-R_GxGuA13o/s640/IMG_20180828_183804814.jpg" width="640" /></a></div>
<br />
<br />
Building something which replicated the entire controller was somewhat ambitious, even if the theory is relatively easy to understand, so I decided my own controller should reproduce only a subset of the controls I most use on the Mixtrack Pro.<br />
<br />
<b>Where to start?</b><br />
So the first thing to do was understand the basic representation provided by MIDI data. This is actually quite straight forward - there are digital commands, on/off data which represents when a note on a keyboard has been pressed or released, and contains a value representing the note itself, such as A#3. These are employed by the Mixtrack as button pushes, for instance pressing and releasing the play button will send a note on and note off signal for B3. This is mapped inside Traktor, so that it knows that it's expected to play or pause the music.<br />
Then there are analogue inputs. MIDI allows for analogue controls such as pitch bend, volume, pan and many custom assignable parameters, which are sent every time a control is moved or updated. The first bit of a MIDI byte is the status bit, indicating what type of data is being sent - therefore the control data is mapped to the values 0 - 127 in the remaining 7 bits. When a control is moved its current value is sampled then sent as MIDI data along with a descriptor of the control to which it belongs.<br />
<br />
<b>The design.</b><br />
The next step of the plan was to figure out how to best implement this in hardware. The first thing that sprang to mind was the trusty ATMega microcontroller (I use these almost every day at work) so the Arduino platform proved to be ideal. These microcontrollers not only have digital inputs I could use for the buttons, but also analogue to digital converters (ADCs) which can be used to sample the analogue sliders and knobs. An Arduino neatly packages this all up, not only on a ready made board, but also in software support. There are many great libraries available for it and I thought I could safely assume there would be a MIDI library to save me much of the work. As it turns out, I was correct :)<br />
<br />
The Arduino Uno board is probably the most popular of all the Arduinos, certainly popular enough that I had at least one spare lying around. It also has another advantage, in that the board connects to the PC for regular development via a USB socket. The Uno actually has a second microcontroller onboard which provides an interface to the UART of the main microcontroller so that data can be sent via RS232 over USB. As it happens this second microcontroller is programmable via the 6 pin ISP header on the board using an AVR ISP MKII programmer. The <a href="http://www.dimitridiakopoulos.com/hiduino" target="_blank">HIDUINO</a> project provides software which can be uploaded to this microcontroller to make it act like an HID MIDI device, and <a href="https://en.wikipedia.org/wiki/Human_interface_device" target="_blank">HID</a>s are compatible with windows, linux and macOS. This is fantastically useful as it means that I didn't have to use a traditional five pin DIN MIDI connector when plugging the Arduino into my PC <i>and</i> I could still power the device from a single connection. The only drawback is that with this software you can no longer use the USB socket to upload sketches via the Arduino IDE. This was of little consequence to me, fortunately, as I could use the aforementioned ISP programmer via the onboard header pins, along with Atmel Studio 7, but this is a caveat to watch out for if you do not own a dedicated programmer.<br />
<br />
<b>Peripheral hardware.</b><br />
So, with the ability to sample digital and analogue inputs and send the results over a MIDI connection in place, the next thing to tackle was which hardware I should be using to create the MIDI signals. I mentioned at the start of the post that I was only going to implement a subset of controls, so I looked at what I used most and made a list:<br />
<ul>
<li>EQ - high, middle and bass</li>
<li>Master volume</li>
<li>Channel Volume</li>
<li>Crossfader</li>
<li>Special effects</li>
<li>Play/Pause button</li>
<li>Cue button</li>
</ul>
The 3 EQ controls and channel volume are used twice, as are the play/pause and cue buttons, once for each of the two audio channels. There are also two special effect buttons used for enabling and disabling the FX units in Traktor. This brings the button count to 6 digital inputs in total, and 10 analogue inputs, with rotary potentiometers for the EQ and master volume controls, and sliders for the channel volumes and crossfader. Fortunately, due to my penchant for collecting DJ equipment, I had an old audio mixer from which I could take the channel sliders and crossfader.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE7zIUl2jrAFzureMHxnjxKNB0i0iZxvbRFF7wvmUP5ZOM9s88QhWjUh5_H02Ur29Qfz78oiPP2GLARmOQxUMPgSmL2D8Vw8urI245o9_94DPmPGudwpDjSYSDXIRigvMS7tcDNQgiMH8/s1600/faders.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="648" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE7zIUl2jrAFzureMHxnjxKNB0i0iZxvbRFF7wvmUP5ZOM9s88QhWjUh5_H02Ur29Qfz78oiPP2GLARmOQxUMPgSmL2D8Vw8urI245o9_94DPmPGudwpDjSYSDXIRigvMS7tcDNQgiMH8/s640/faders.jpg" width="640" /></a></div>
<br />
<br />
The rotary knobs, however, I ordered from Amazon. It's important to note that both the knobs and the sliders use a linear scale, not an logarithmic one which is most commonly found in audio equipment. The old mixer used <a href="http://synthesizeracademy.com/voltage-controlled-amplifier-vca/" target="_blank">VCA</a> (voltage controlled) signals which is why they were linear. If you're not aware of the difference between linear and logarithmic sliders, then it's definitely worth checking out <a href="https://www.audiocheck.net/soundtests_nonlinear.php" target="_blank">this page</a>, which explains why logarithmic sliders are used in audio hardware. As this is a controller we want to control the software in a linear fashion - the DJ software (Traktor in this case) implements the logarithmic scale itself.<br />
For buttons I wanted something a bit fancy, and the best looking ones I could find were actually designed for an arcade cabinet. <a href="https://www.arcadeworlduk.com/" target="_blank">Arcadeworld UK</a> do a great selection, and even have buttons which include LEDs - so of course I had to have those ;)<br />
<br />
<i>A note on the power:</i><br />
It's always worth taking into consideration just how much current a project like this is likely to draw. As I'm powering the Arduino via the USB port, the supply can provide a maximum current of 500mA. A quick bit of maths (and good old Ohm's law) will let us calculate just how much current is going to be drawn:<br />
<br />
5v / 10kOhm for each of the rotary controls and two channel sliders = 0.5mA * 9 = 4.5mA<br />
The crossfader is 50kOhm (for no reason other than that's just what I happened to have) so 0.1mA<br />
<br />
This gives a total of 4.6mA, plus the 6 LEDs which are rated at 5mA according to the button's spec sheet. One of these per channel is fine (an Arduino channel is rated 40mA max) and assuming all 6 LEDs are on at once this gives us a total of 30mA current draw for the LEDs. Add that to our analogue inputs to get 34.6mA and we find that this is well within the 500mA PSU limit. Hooray!<br />
<br />
<b>Hurdle number one.</b><br />
Now, while I was getting carried away ordering all these parts, I'd also started to assemble something of a prototype using the sliders and some old clicky buttons I had lying around. This is where I hit the first snag. While the Uno has more than 6 digital inputs, it doesn't have enough to also light up those fancy button LEDs. It certainly doesn't have 10 analogue inputs. Hum. The solution to this was the <a href="https://www.sparkfun.com/products/13906" target="_blank">Sparkfun 8 way multiplexer</a>, which allows up to 8 signals to be used on a single Arduino input. This works by sending a binary value between 0 - 7 over 3 connections (each one representing a single bit) to the board to select which input to listen to, which is then forwarded to a common connection to the Uno. With only four connections to this board I had enough digital inputs for the buttons, with two to spare, and enough remaining digital connections on the Arduino itself to drive the LEDs. The <a href="https://www.sparkfun.com/products/9056" target="_blank">16 way bigger brother</a> of this board provided plenty of inputs for the analogue controls, with six spare in case I ever decide to upgrade in the future...<br />
Both boards are operated in the same way so they can share the same set of selection pins when connecting to the Arduino.<br />
<br />
Phew.<br />
<br />
With the prototype up and running it turned out to be pretty close to the final design. Here is the schematic for the overall circuit, and a fritzing wiring diagram:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX1JDviyrglGnhsf3kFjJU2LR1oqqg8K_x1dhbQTT4efJdDJ25gt4wTGA0zIHvcHnrqxNJ8t8d0g0CyKGv6Yb-hWpVwIG2N3S-mbSeYAB2d7GV96cq6cWOJpktc8Az2eNfrs5MJJh2kQg/s1600/midicontroller_schem.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="747" data-original-width="1600" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX1JDviyrglGnhsf3kFjJU2LR1oqqg8K_x1dhbQTT4efJdDJ25gt4wTGA0zIHvcHnrqxNJ8t8d0g0CyKGv6Yb-hWpVwIG2N3S-mbSeYAB2d7GV96cq6cWOJpktc8Az2eNfrs5MJJh2kQg/s640/midicontroller_schem.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIc1PN12o0vU4_YiIVMdduzcfgNFIms8Akwcffa_hnWliTijX_fKTnx9QmH3pZxWlkRFt_PHyJUZDVZ1H-kpB29WzVNj3qwcZXXYbOGtKwcaI4G791JMO_htBExEliBcLc5DS2qxhB72c/s1600/midicontroller_bb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1280" data-original-width="1600" height="512" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIc1PN12o0vU4_YiIVMdduzcfgNFIms8Akwcffa_hnWliTijX_fKTnx9QmH3pZxWlkRFt_PHyJUZDVZ1H-kpB29WzVNj3qwcZXXYbOGtKwcaI4G791JMO_htBExEliBcLc5DS2qxhB72c/s640/midicontroller_bb.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
The fritzing project file is included in the <a href="https://github.com/fallahn/midicontroller" target="_blank">project repository</a>.<br />
<br />
<b>Sending useful data.</b><br />
With everything wired together, albeit in a bit of a jumbled mess on my desk, I could start work on the software. I already had the <a href="http://www.dimitridiakopoulos.com/hiduino" target="_blank">Hiduino</a> software loaded up on the USB controller, so I fired up Atmel Studio 7, created a new project and linked it to the Arduino core library.<b> </b>I decided to use the Arduino core partially for convenience, but also because I wanted to use <a href="https://github.com/FortySevenEffects/arduino_midi_library" target="_blank">this MIDI library</a> which greatly simplified sending MIDI data without having to implement the protocol myself. To decide which data to send I figured I might as well copy the MIDI layout of the Mixtrack Pro - after all Traktor was already set up to use it, and it is also a known reliable mapping. Figuring out what data was output by which control was made considerably easier by a utility called MIDI OX, which you can download <a href="http://www.midiox.com/" target="_blank">here</a>. This simple program outputs any incoming MIDI data to a console style window so you can easily see what's being sent and received over a MIDI connection. It was also at this point I discovered that, when sending back data via MIDI OX, the LEDs were controlled by note on/off events. Having realised this I made sure to handle incoming MIDI data on the Arduino. With all the mappings now worked out I made sure to note them all in the source code, as I'm pretty sure my future self will be very grateful.<br />
<br />
I won't list the entire source code here, this post is already long enough, but you can download it <a href="https://github.com/fallahn/midicontroller" target="_blank">via the repository</a>. The overall technique though is as follows:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">for each analogue input:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> select the input via the multiplexer</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> sample the current value of the input</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> if the input value has changed since the last sample, send it via a MIDI signal</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">repeat for each digital input</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">handle any incoming MIDI signals and forward them to LEDs as necessary.</span><br />
<br />
The premise is pretty simple, and made easy to implement thanks to the existing libraries (and the hard work of their authors!). With the prototype up and running, and my excitement mounting, it was time to turn my hand to designing the enclosure which will be the topic of my next post which you can find <a href="https://trederia.blogspot.com/2018/09/building-custom-midi-controller-part-2.html" target="_blank">here</a>.<br />
<br />
<a href="https://github.com/fallahn/midicontroller" target="_blank">Source code and Fritzing diagrams.</a> Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-62590641278609845082018-03-29T18:51:00.000+01:002020-05-12T10:43:49.650+01:00Castle Clamber Launches 12th April 2018!<iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/xT01pMm25SM" width="560"></iframe>
<br />
<br />
<br />
<br />
You heard right folks! I am very proud to announce that my first commercial game will be available to <a href="http://store.steampowered.com/app/770050/Castle_Clamber/" target="_blank">buy on Steam</a> on Thursday 12th April 2018 for Windows and macOS. A HUGE Thank You goes to so many people who have helped not only on the project directly, but by supporting me for all the years I've been floundering around in the world of game dev (just take a look at this blog's history!) and throughout my life in general. This has been a true labour of love and enthusiasm, created entirely in my spare time with the help of communities such as <a href="https://www.sfml-dev.org/" target="_blank">SFML</a> and <a href="https://opengameart.org/" target="_blank">Open Game Art</a>.<br />
<br />
Special mention should go to (in no particular order): <br />
<ul>
<li>Dorus Abeln</li>
<li>Lukas DĆ¼rrenberger</li>
<li>Jean-SĆ©bastien Fauteux</li>
<li>Darren Ferrie</li>
<li>Jason Mercado </li>
<li>Josh Mercier</li>
<li>Jonny Paton</li>
<li>Jon Povey</li>
<li>Stefan Schindler</li>
<li>Tobias Walsh</li>
<li>Tobias Widlund </li>
</ul>
And, while this is a significant milestone, it is by no means the end of the road. I have some future updates planned for Castle Clamber including, but not limited to: Steam Workshop support for user maps, time trial mode (with speed runner friendly times per map) and Super Hardcore Mode! There's no roadmap in place for these updates just yet, but you can keep track of any game dev related updates via my twitter handle <a href="https://twitter.com/TrederiaGames" target="_blank">@TrederiaGames</a><br />
<br />
The Castle Clamber Steam page can be found <a href="http://store.steampowered.com/app/770050/Castle_Clamber/" target="_blank">here</a> where you can grab it for 2.99ā¬ / Ā£2.09 / $2.99 with a 40% discount at launch!<br />
<br />Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-71969062682062946882018-02-23T19:00:00.000+00:002018-02-23T19:00:05.058+00:00Castle Clamber - new game coming to Steam!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9extLS_nAyES6bs-v-ovfA8hGB49ggbCIiIEgxyOZHbLN65Mt7uQM4ceM4SRhtZXOCDvOKM6oj4fNKvp6ilSHs3ksXvmQdcWRtSJSHyfsExMWEoBV3uZgVKNJvxzOZghzG7h96ZGvQs/s1600/capsule_616x353.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="353" data-original-width="616" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH9extLS_nAyES6bs-v-ovfA8hGB49ggbCIiIEgxyOZHbLN65Mt7uQM4ceM4SRhtZXOCDvOKM6oj4fNKvp6ilSHs3ksXvmQdcWRtSJSHyfsExMWEoBV3uZgVKNJvxzOZghzG7h96ZGvQs/s1600/capsule_616x353.jpg" /></a></div>
<br />
I ain'tn't dead!<br />
<br />
In a <a href="http://trederia.blogspot.com/2017/08/crogine-cross-platform-mobile-oriented.html" target="_blank">previous post</a> I talked about the crogine framework I'd been working on and how I'd like to take some of its features, particularly the ECS, and merge them into xygine. Well, that quickly escalated....<br />
<br />
After a huge (and very successful) refactor of xygine, dubbed xyginext to differentiate it from its existing, incompatible, incarnation, I started on a new demo to include in the <a href="https://github.com/fallahn/xygine" target="_blank">repository</a> as reference for anyone wanting to use the framework. I based it on some old arcade games from the 80s and 90s which I remember fondly from my childhood, adding modern features such as network play, powered by the <a href="http://enet.bespin.org/" target="_blank">enet library</a> from <a href="http://sauerbraten.org/" target="_blank">Sauerbraten</a>. As it turns out it was pretty fun to play, as well as to develop, so the past 6 months I've been busy beavering away, fleshing out the project and readying it for release on steam, for Windows, linux and macOS. It also uses <a href="https://github.com/fallahn/tmxlite" target="_blank">tmxlite</a> ;)<br />
<br />
It would be remiss of me not to mention all the help I've had from the <a href="http://chat.sfml-dev.org/" target="_blank">SFML community</a> of course, particularly the thorough play testing by Rosme and all of Jonny's contributions to xygine, the build system, the editor, the CI... so thanks guys, it means a lot! There's no concrete release date for the game, dubbed 'Castle Clamber', yet - but I'm looking to a March/April window.<br />
<br />
The game revolves around a princess at the top of her castle tower, who has woken in the middle of the night and fancies something to eat. The goal is to climb the tower, built from 30 levels, collecting food and squashing any bad guys who might get in the way. On release there will be three towers, each with its own theme, bringing the total number of levels to 90. The next tower is unlocked when the previous one is completed. There are plans to eventually bring steam workshop support to the game so users can create their own towers for everyone to play.<br />
<br />
<br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="en">
Didn't even see it coming! <a href="https://t.co/54KXsmVhQ7">pic.twitter.com/54KXsmVhQ7</a></div>
ā Trederia Games (@TrederiaGames) <a href="https://twitter.com/TrederiaGames/status/966435711506370560?ref_src=twsrc%5Etfw">February 21, 2018</a></blockquote>
<script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>
<br />
You can follow the progress of the game on twitter <a href="https://twitter.com/TrederiaGames" target="_blank">@TrederiaGames</a> and find the updated xygine <a href="https://github.com/fallahn/xygine" target="_blank">repository on github</a> as before.<br />
<br />
Oh, and Happy Birthday Doris! Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-87192981156238431642017-08-29T19:00:00.000+01:002017-08-29T19:00:26.511+01:00crogine: cross-platform mobile oriented game frameworkAfter I wrapped up <a href="https://gamejolt.com/games/doodlebob/256272" target="_blank">DoodleBob</a> at the beginning of the year, I wanted to find a project which would push me to learn something new. A few people pointed out to me that Bob would be a great little time waster if it were available on mobile devices. Unfortunately <i><a href="https://github.com/fallahn/xygine" target="_blank">xygine</a></i>, as it is SFML based, won't run on anything outside of desktop systems. As great as I think SFML is its mobile support, to put it bluntly, is a bit pants. So, then, a chance to not only learn mobile development but some <a href="https://www.libsdl.org/download-2.0.php" target="_blank">SDL2</a> too! Thus, the concept of <i>crogine</i> (the CROss-platform enGINE) was born.<br />
<br />
In previous posts I've commented on some of the development issues when creating <i>crogine</i> (and the games which are powered by it), including an explanation of how I set up <a href="http://trederia.blogspot.co.uk/2017/03/building-sdl2-for-android-with-visual.html" target="_blank">Android building in Visual Studio</a>. Here, then, I shall only summarise the overall structure of <i>crogine</i>.<br />
<br />
<b>The Framework</b><br />
Most importantly I wanted to make sure I used what I had learned when writing <i>xygine</i> and improve on it where I could. The base <span style="font-family: "Courier New",Courier,monospace;"><a href="https://github.com/fallahn/xygine/wiki/Getting-Started" target="_blank">App</a></span> class and <a href="https://github.com/fallahn/xygine/wiki/States" target="_blank">state stack mechanism</a> remain more or less unchanged, as does the <span style="font-family: "Courier New",Courier,monospace;">Event</span> and <span style="font-family: "Courier New",Courier,monospace;"><a href="https://github.com/fallahn/xygine/wiki/Messages" target="_blank">Message</a></span> systems. Starting a new project using the framework should not only be relatively quick and easy, but also familiar, and what I had with <i>xygine</i> worked well. I also wanted to make sure the interfaces to the core classes were familiar too, so it's possible to see a strong SFML influence on the class names and members therein. It meant reinventing many of the OpenGL classes such as textures and shaders, but the learning and insight proved highly valuable.<br />
<i>xygine's</i> biggest fault, in my opinion, was the poorly designed <a href="https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system" target="_blank">ECS</a>, which, at the time, was heavily influenced by the use of Unity. It was pretty inflexible, required a lot of boiler plate every time a new component was created and, er, didn't really have any systems as such. This is the biggest change in <i>crogine</i> (and what <i>crogine</i> has taught me will, undoubtedly, find its way back into <i>xygine</i>) where, thanks to some research and invaluable suggestions by people over at <a href="http://chat.sfml-dev.org/" target="_blank">#sfml</a>, the ECS is completely new, and takes a cleaner, <a href="https://en.wikipedia.org/wiki/Data-oriented_design" target="_blank">data oriented</a> approach. Now components are more or less pure data, small structs where possible, with accessors in classes only where I feel it makes sense. Processing the components is moved out into corresponding systems, which not only makes for much neater code, but far better <a href="http://gameprogrammingpatterns.com/data-locality.html" target="_blank">data locality</a>, in turn providing much better performance, essential on mobile platforms.<br />
Splitting the systems from the components also means that the ECS is far more modular, systems such as the 3D renderer can be easily implemented more than once, for example a GLES2 renderer for mobile, and an OpenGL 4.x renderer for more powerful desktop systems. Either one of these can be instantiated at runtime with absolutely no change to any of the required components, meaning that a single game in development can target multiple systems while still taking advantage of specific hardware. Audio systems can be swapped in and out too, favouring either OpenAL or SDL_mixer - and, with an open interface, custom audio renderers could be written should the need arise.<br />
<br />
I have introduced the concept of 'directors', classes which sit within a scene, but only observe it through events and messages. The directors can, when needed, then send commands to select groups of entities through the <span style="font-family: "Courier New",Courier,monospace;">Command</span> system. Following a data-driven approach, <i>crogine</i> loads assets such as particle systems, sprites and models via external configuration files. This allows definitions for materials, textures and meshes to be easily updated in a text editor, without having to recompile any software.<br />
<br />
<b>Multi Dimensional</b><br />
Writing a framework from the ground up was also the perfect opportunity to properly develop complimentary 2D and 3D rendering systems. In <i>xygine</i> I created a 3D rendering system which sat on top of SFML in a wobbly kind of way - so much so that due to compatibilty issues with MacOS the 3D renderer only ran on Windows and linux. Here, in <i>crogine</i>, the <span style="font-family: "Courier New",Courier,monospace;">Scene</span> class has been written in such a way that simply replacing the projection of the active camera switches the output between 2D and 3D. Multiple scenes can run alongside each other happily, so a single scene can be dedicated to the 3D rendering of the game world, while a second scene, using an orthographic projection, renders a HUD and menus over the top. This also brings the full flexibility of the ECS for rich and complex scenes to the UI. <i>xygine's</i> dedicated UI classes are nowhere to be found, as everything is now entity based. This can be used to create some cool effects such as 3D menus, and HUDs which interact directly with the game world. With all this in mind it is likely that future revisions of <i>xygine</i> will drop 3D in favour of returning to its 'xy' roots, and leave the 3D game development to <i>crogine</i>.<br />
<br />
<b>But...</b><br />
Nobody, and nothing is perfect. While I'm pleased with much of what I have, particularly mobile performance which, quite frankly, surprised me, the API is a little uneven. When I started I had a pure data oriented ECS in mind, and those early components such as meshes, models and shaders all have very public data members. As development went on I decided I still preferred a more encapsulated interface afforded by OOP, so I tried to blend the two where I could. Components are still very much pure data, but the public interface has tigher control over its access.The latter has worked out OK, but has left some of the earlier code in need of refactoring. <i>crogine</i> is still very much in development in some places too, with incomplete function definitions here and there, and rogue comments which weren't updated with subsequent refactorings, making them occasionaly vague and misleading. The main driving force behind development has been the creation of two games, one of which is (yet another) remake of Threat Level. Because of this the development of <i>crogine</i> is led very much by the needs of the game in its current state. Compared to <i>xygine</i> there is also little documentation outside of the included doxy comments.<br />
<br />
<b>Future additions</b><br />
While <i>crogine</i> is already in good shape the underlying SDL library provides a few more interesting features which I'd like to eventually experiment with. One of those is haptics, force feedback via controllers, and the vibration function on many mobile devices. I'd also like to improve the network support, preliminary connections are provided by <a href="http://enet.bespin.org/index.html" target="_blank">enet</a>, but multiplayer games are something I'd really like to develop down the line. As previously mentioned I also want to take what I've learned of ECS development and move it back into <i>xygine</i> via its own development branch. I also plan to fill the <i>crogine</i> wiki with as much documentation as I can... before I forget how it all works ;)<br />
Plenty to keep me busy then and, I suspect, away from any reasonable blog posting in the near future. To fill some of that gap here's a video of <i>crogine</i> in action, demonstrating the current state of Threat Level:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/y1atve3Bei8" width="560"></iframe>
<br />
<i>crogine</i> is open source, as is Threat Level, the sources for which you can find over at <a href="https://github.com/fallahn/crogine" target="_blank">github</a>.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com3tag:blogger.com,1999:blog-6421765335659381070.post-40874992958931707332017-05-28T10:39:00.000+01:002017-05-28T10:39:15.062+01:00New Gamejolt PageWhile Bob and Pseuthe are both quite happy residing on <a href="http://itch.io/">itch.io</a>, I thought I'd branch out a bit and also add them to my new <a href="http://gamejolt.com/@fallahn/games">Gamejolt page</a>. Gamejolt, for those who don't know, is an indy-friendly community for hosting game projects, not unlike itch. The main difference is that it offers its own API for integration with its <a href="http://gamejolt.com/client">game client</a> (itch also has a game client, <a href="https://itch.io/app">check it out</a> if you want to find some obscure gems!) which provides services such as high-score tables and trophies which can be shared with the rest of the Gamejolt community. Both <a href="http://gamejolt.com/games/doodlebob/256272">DoodleBob</a> and <a href="http://gamejolt.com/games/pseuthe/256275">Pseuthe</a> are perfect candidates for these features, so hopefully sometime in the near future I'd like to take advantage of these by adding trophies to both games, and an online high-score for Pseuthe. They'll both remain on itch.io, although downloading them from there will mean they won't feature the Gamejolt integration.<br />
<br />
If you want to find out more about these features so you can add them to your own games, check them out <a href="http://gamejolt.com/api/doc/game">here</a>.<br />
<br />
In other news I've been spending the last few months developing a new game engine - a spiritual successor to <a href="https://github.com/fallahn/xygine">xygine</a> - with mobile platforms in mind. I've switched from sfml to SDL2 for the cross platform parts, as it provides much more fine grained control over the graphics rendering, and has (in my opinion) far superior mobile platform support. I've not completely abandoned sfml, and, as it's API is beautifully crafted (and one of its strongest points), its influence can be seen heavily in the API of the new engine. In fact, as a 'spiritual successor' the API is very similar to that of xygine (which, of course, was born of sfml ;) ) where I've tried to pretty much port all its good features and improve everywhere else. The biggest change (apart from the underlying switch to SDL2) has to be the new <a href="https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system">ECS</a> which tries to take a <a href="http://gamesfromwithin.com/data-oriented-design">data-driven approach</a> to the much lauded entity-component system. I've been swayed by other programmers on <a href="http://chat.sfml-dev.org/">IRC</a> (you know who you are...) and the copious amount of reading and research I've done. I'm very pleased with the apparent increase in performance while having maintained the easy to use interface to the framework. When everything is a bit more... baked I'll go into detail in a dedicated post (as well as releasing the source), but for now I'll share a snippet of some work-in-progress remixing of an <a href="https://www.youtube.com/watch?v=L61ssYvp2Bg">old favourite</a> which I've been using to develop the engine.<br />
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/HhiaSGSRryQ" width="560"></iframe>Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-45597897353858428172017-04-16T11:44:00.000+01:002017-04-16T11:44:17.162+01:00Download DoodleBob for linuxPenguin lovers rejoice! It's been a while coming but I've finally pulled my finger out and uploaded linux binaries to the <a href="https://fallahn.itch.io/doodlebob">itch.io page</a>. Bob has always been linux friendly, but up until now it required building from <a href="https://github.com/fallahn/xyExperimental/tree/master/DoodleChum">source</a> - this is no longer the case! If you're an x64 linux user you can now <a href="https://fallahn.itch.io/doodlebob">download him directly</a>.<br />
<br />
Thanks goes to Doris for his help in creating the distributable archive.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-54092201014712936862017-03-17T17:00:00.000+00:002017-03-17T18:21:18.950+00:00Building SDL2 for Android with Visual StudioFor the last year or so Visual Studio 2015 (and now 2017) has had the ability to build Android applications using native code compiled with the Android ndk. This really appeals to me as I tend to favour the Visual Studio environment, so I thought it was finally time to see what I could do. Unfortunately the documentation for Visual Studio specific native development seems to have fallen a little by the way, or at least is very dry on the MSDN. I am, in particular, interested in game development so it made sense to me to create an SDL2 based project as SDL supports Android from the go - but setting it up in Visual Studio required a lot of trial and error. This post, then, is my attempt to best document what I did to get the project building and running on my now aging Moto G so that others - and most likely my future self - may possibly be spared the hair pulling I've endured this last week.<br />
<br />
<b>Project Overview</b><br />
SDL2 projects on Android differ slightly to those created for desktop applications on platforms such as linux or Windows. Generally in these scenarios you link to SDL as you would any other library and start coding away. On Android, however, SDL2 does not run as a native activity application, rather it needs to be linked and invoked via a java activity so that hardware events such as input may be correctly handed down to the native code. Fortunately the SDL author provides the java source file needed within an example ndk project - it just needs a little work to include it in Visual Studio. This means that, at the very least, you need to create two Visual Studio projects - plus a third to contain your own code. If you're also writing a library to share code with other platforms the project count jumps to four, or more if you wish to include libraries such as <i>SDL_image</i>... I shall be concentrating on describing what it takes to build SDL2, link it to an Android java project, and have it display something with some custom code, starting with the SDL2 library. Other libraries should follow the same process more or less.<br />
<br />
<b>Building SDL2</b><br />
Assuming that Visual Studio 2015 or 2017 is installed along with the cross-platform development options (make sure to check this when installing VS) the first thing to do is create an empty solution, then add a new project. From the templates menu select <i>Dynamic Shared Library</i>. Call the project SDL2 so that Visual Studio will automatically give the library file the correct name when it gets compiled.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS6HTJYlGZ8C_YjovpaVQch6BB3ujIBn4ksZkD8kA4g554OoBLwAXSnwqqMKvG0xAtpuhMkn18XDAMs6VSNohGSBWsjgelsmyCRMakZomVE-yhj69r65EI_mbLBT_mW3w79FL9cHAll5U/s1600/new_sdl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS6HTJYlGZ8C_YjovpaVQch6BB3ujIBn4ksZkD8kA4g554OoBLwAXSnwqqMKvG0xAtpuhMkn18XDAMs6VSNohGSBWsjgelsmyCRMakZomVE-yhj69r65EI_mbLBT_mW3w79FL9cHAll5U/s320/new_sdl.png" width="320" /></a></div>
<br />
<br />
This will provide the basis for what's needed to build SDL2 as a shared library, although a few settings will need to be changed. It's also worth noting that the project has four configurations, <i>ARM, ARM64, x86</i> and <i>x64</i>. Each of these will need the same modifications applied if you intend to use them, although I'll only cover the <i>ARM</i> configuration here. The <i>x86</i> configuration is useful because these builds can be used with the x86 HAXM accelerated version of the Android emulator which runs much more quickly on a desktop computer than the ARM version.<br />
The first thing to do is add the SDL2 source code to the project. This can be downloaded as an archive from the <a href="https://www.libsdl.org/download-2.0.php">website</a>, currently at version 2.0.5, although other versions should be similar if not the same. Extract the archive and copy the <span style="font-family: "courier new" , "courier" , monospace;">src</span> and <span style="font-family: "courier new" , "courier" , monospace;">include</span> directories to the Visual Studio project directory. In Visual Studio remove the <span style="font-family: "courier new" , "courier" , monospace;">SDL2.h</span>, <span style="font-family: "courier new" , "courier" , monospace;">SDL2.cpp</span> and <span style="font-family: "courier new" , "courier" , monospace;">pch.h</span> files generated by the template. In the SDL2 folder extracted from the archive is an Android.mk file. Open this in a text editor as it contains information needed for the Visual Studio project, including a list of required source files. Using the make file as a guide add each one of the source files to the project in Visual Studio by right clicking it, selecting 'Add Existing' and selecting the corresponding files from the <span style="font-family: "courier new" , "courier" , monospace;">src</span> directory that was copied earlier (not the source directory in the extracted archive folder).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWMP0SBhQgPY4t7tellU2zEgEQck9JxAPkMoDRJSrIvRKFf1mqS47X2eQxPFhMz0W0VgDyElTXaEFlzXaYlEX2XA_nPpO9KKr0T5pPlZg0GoITHp63tOQhQpcgh71dygaXHSDgCL9N74M/s1600/source_list.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWMP0SBhQgPY4t7tellU2zEgEQck9JxAPkMoDRJSrIvRKFf1mqS47X2eQxPFhMz0W0VgDyElTXaEFlzXaYlEX2XA_nPpO9KKr0T5pPlZg0GoITHp63tOQhQpcgh71dygaXHSDgCL9N74M/s320/source_list.png" width="242" /></a></div>
<br />
<br />
I also add filters to represent the directory structure of the source folder, but this isn't strictly necessary.<br />
The Android.mk file also contains a compiler flag, and a list of libraries SDL2 needs to link to. In the Visual Studio project properties add GL_GLEXT_PROTOTYPES to the C/C++ Preprocessor definitions. This is the equivalent of the LOCAL_CFLAGS parameter in the Android.mk file. Under Linker->Input in the Visual Studio project properties add the name of all the libraries listed under the LOCAL_LDLIBS property in the Android.mk file. Visual Studio also requires the maths library to be linked for some reason, presumably <span style="font-family: "courier new" , "courier" , monospace;">ndk-build</span> automatically adds this when using the Android.mk file. This is added by typing <span style="font-family: "courier new" , "courier" , monospace;">m;</span> at the front of the list of libraries (order is important).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHPGUHfK_D-Dob0aQV2GgklwvaPH9woqpv_nbvytG3PWV28MtljYupCBTGLA_lSoekjXvYNQfl5a0jbByQsDplurcioR7VaeXa5cwlP3x2MCWHjjGhxfmBDfMKYnom4Z9HdS8MV_N62Fo/s1600/preproc_libs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHPGUHfK_D-Dob0aQV2GgklwvaPH9woqpv_nbvytG3PWV28MtljYupCBTGLA_lSoekjXvYNQfl5a0jbByQsDplurcioR7VaeXa5cwlP3x2MCWHjjGhxfmBDfMKYnom4Z9HdS8MV_N62Fo/s320/preproc_libs.png" width="320" /></a></div>
<br />
<br />
Finally add the local SDL2 include directory to Additional Include Directories under C/C++->General, and then under Precompiled Headers select Not using Precompiled Headers.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8rsz-JmWkqko-qk2gyBXh_dvXzmI8LOHf6eey4EPO14T9zia-bkqcmXCrDPn-3l4TYzxNW0aCQK0js8NCLyMryNIL5xJFJMpQzPsDxzDJD8wru1p0mIof-f_UpRommGPgKVB0dwJEGoA/s1600/include_pch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8rsz-JmWkqko-qk2gyBXh_dvXzmI8LOHf6eey4EPO14T9zia-bkqcmXCrDPn-3l4TYzxNW0aCQK0js8NCLyMryNIL5xJFJMpQzPsDxzDJD8wru1p0mIof-f_UpRommGPgKVB0dwJEGoA/s320/include_pch.png" width="320" /></a></div>
<br />
<br />
And that's it for the SDL library. Right click on the project in the explorer tree and select Build (or press F7) and Visual Studio should compile the source and output a file called <span style="font-family: "courier new" , "courier" , monospace;">libSDL2.so</span>.<br />
<br />
<b>Creating the activity</b><br />
To run SDL2 on Android a second project needs to be created which provides a java activity that invokes the native code. The code for this is provided in the SDL2 source archive but, again, requires some setup to work with Visual Studio. Add a new project to the solution, this time selecting the template for <i>Basic Application</i>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwV_NhcvUTtqiY5TUvB0GBr78F6iiFpFTvnNlSu8ZsXSb2835uJrl2s1NUAdXAZFAvihoDIxx2F9Kl27CtjtFLS78Y-nUf66EvE1H8S03kgnysFOM4dX0gAwki87Q4eQu65_mVPXN51b8/s1600/new_basic_app.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwV_NhcvUTtqiY5TUvB0GBr78F6iiFpFTvnNlSu8ZsXSb2835uJrl2s1NUAdXAZFAvihoDIxx2F9Kl27CtjtFLS78Y-nUf66EvE1H8S03kgnysFOM4dX0gAwki87Q4eQu65_mVPXN51b8/s320/new_basic_app.png" width="320" /></a></div>
<br />
In the solution explorer right click on the project references, and select SDL2. This tells Visual Studio that it should build and package the SDL2 library in this application. In the folder which was extracted from the SDL2 source archive is a directory named <span style="font-family: "courier new" , "courier" , monospace;">android-project</span>. Inside this is a folder called <span style="font-family: "courier new" , "courier" , monospace;">src</span>, the contents of which need to be copied to the <span style="font-family: "courier new" , "courier" , monospace;">src</span> folder found inside the new directory containing the Visual Studio application project. This folder contains the java source file required to invoke SDL2 in the application. Back in Visual Studio click 'Show All Files' in the Solution Explorer so that the <span style="font-family: "courier new" , "courier" , monospace;">org</span> folder (copied from the archive <span style="font-family: "courier new" , "courier" , monospace;">src</span> to the project <span style="font-family: "courier new" , "courier" , monospace;">src</span>) appears in the tree. Right click it and select 'include in project'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMkDdcMkxGtdLT_tcJStGOOBmOhAzFpuo15OeYivQ-d5D88dgeA_SD3aIhNBzzwbaMH33gxz2I7KEBxVtB2xo3ZYrF3XHw7K8muTXOxi-um7M6ynLvjdaf9HpXNFXvPJG9OL7hFkEB5Uw/s1600/include_SDLActivity.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMkDdcMkxGtdLT_tcJStGOOBmOhAzFpuo15OeYivQ-d5D88dgeA_SD3aIhNBzzwbaMH33gxz2I7KEBxVtB2xo3ZYrF3XHw7K8muTXOxi-um7M6ynLvjdaf9HpXNFXvPJG9OL7hFkEB5Uw/s320/include_SDLActivity.png" width="226" /></a></div>
<br />
<br />
A java file under <span style="font-family: "courier new" , "courier" , monospace;">org.libsdl.app</span> called <span style="font-family: "courier new" , "courier" , monospace;">SDLActivity.java</span> should now be included in the project. Import this to the application java file which was created with the project, then remove the 'hello world' example code. Modify the class so that it now extends <span style="font-family: "courier new" , "courier" , monospace;">SDLActivity</span>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6fyJU8_OMgLcJVtNVmydIJmeH2KU62FZjj8qlK74reFbBW_YAKErpJOGlZi-duaEVhMZ3mhF6LZBB2ILt94x2UZnLE44x2xiaSHnb6WinhdfpVL8SW0OypqV7t_6uYp-ZKOXJadrfavI/s1600/java_source.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6fyJU8_OMgLcJVtNVmydIJmeH2KU62FZjj8qlK74reFbBW_YAKErpJOGlZi-duaEVhMZ3mhF6LZBB2ILt94x2UZnLE44x2xiaSHnb6WinhdfpVL8SW0OypqV7t_6uYp-ZKOXJadrfavI/s320/java_source.png" width="320" /></a></div>
<br />
<br />
Make sure to call <span style="font-family: "courier new" , "courier" , monospace;">super.onCreate()<span style="font-family: "arial" , "helvetica" , sans-serif;">.It's this function which will invoke the SDL native code</span></span>. Building the project should now automatically build and package the SDL2 library and output an apk file. On its own, however, this apk is not much use. The final step is to add and invoke your own code.<br />
<br />
<b>Adding your own code</b><br />
To make the application actually do something a third project is required which contains the code for your game or application. Setting this project up is pretty generic, and once it's done you can continue to write your software in pretty much the same way as you would for any other platform. Add another project to the Visual Studio solution, again selecting <i>Dynamic Shared Library</i>. This can be named anything you like, but as it will contain the entry point to the native code SDL2 expects it to be named '<span style="font-family: "courier new" , "courier" , monospace;">main</span>' by default.<br />
In the project properties add the SDL2 include directory to the include path (as well as any of your own include directories). Disable using precompiled headers.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP8Dmjjk-ZSGEMLQmI3VRWrBJ2iQ-gjIDZNzBDqgk9vMhEbqZy2AiOLxs26nIjv24AT2D2SVbVdob-HpzLaeBm9WEbFQaKemUGkvuBwMZfiXR77kbczvXnOLpFMugLe0Bl2OQFLJ9uV9g/s1600/main_property01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP8Dmjjk-ZSGEMLQmI3VRWrBJ2iQ-gjIDZNzBDqgk9vMhEbqZy2AiOLxs26nIjv24AT2D2SVbVdob-HpzLaeBm9WEbFQaKemUGkvuBwMZfiXR77kbczvXnOLpFMugLe0Bl2OQFLJ9uV9g/s320/main_property01.png" width="320" /></a></div>
<br />
<br />
To link SDL2 to the project go to Linker->General and add a new path to Addition Library Directories: <span style="font-family: "courier new" , "courier" , monospace;">$(SolutionDir)$(Platform)\$(Configuration)</span><br />
This should automatically evaluate to the output directory of the SDL2 project for each of the platform configurations.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16o713Z0kDjKVuVZhlrNKESmh4fTZW9JwvG-NkNZULuFS3j1mwYvrrE0zIlSrIMlW_sGxVhAa6iGD2i5leqBXx-UvNKtR9S23CQ5-V3BvBhW8MnviFpJ0EfQF8DkIYl0MR1FmoZfpCnI/s1600/additional_lib.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16o713Z0kDjKVuVZhlrNKESmh4fTZW9JwvG-NkNZULuFS3j1mwYvrrE0zIlSrIMlW_sGxVhAa6iGD2i5leqBXx-UvNKtR9S23CQ5-V3BvBhW8MnviFpJ0EfQF8DkIYl0MR1FmoZfpCnI/s320/additional_lib.png" width="320" /></a></div>
<br />
<br />
Under input add the same libraries as the SDL2 project, followed by SDL2 itself.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW-TnNIQ4gUFkXh9yext4Ks2ZvwNiQrowf8DKkDiPV0sx0Q7MKsDtqYnznTdBtJ3P3pwmx3z1IYvtdZd2_2LhiGDRywQhsNMC9rsPCj6wrX5w7Btz5HBP65xMKwhFyugjG_ioLbNKBR9w/s1600/main_property02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="57" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW-TnNIQ4gUFkXh9yext4Ks2ZvwNiQrowf8DKkDiPV0sx0Q7MKsDtqYnznTdBtJ3P3pwmx3z1IYvtdZd2_2LhiGDRywQhsNMC9rsPCj6wrX5w7Btz5HBP65xMKwhFyugjG_ioLbNKBR9w/s320/main_property02.png" width="320" /></a></div>
<br />
<br />
The project template creates three source files containing some boiler plate code. Remove <span style="font-family: "courier new" , "courier" , monospace;">pch.h</span>, and <span style="font-family: "courier new" , "courier" , monospace;">main.h</span> but keep replace the contents of <span style="font-family: "courier new" , "courier" , monospace;">main.cpp</span> with the standard <span style="font-family: "courier new" , "courier" , monospace;">main()</span> entrypoint code. All of your application's code will be initiated from here, and this file can be shared between multiple SDL2 projects, for instance when building for both desktop and android platforms.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimhvSV9s7E_Ce1YizPEOzuVGmIQDuoKjoSd42tqMIU0rJBbJBwpUXWK7F08vCC5xK8w0w4aEU15yz_un3fDHXhdXLBFY4YkVQNfc8HXVrp4vEY11GFlOhj5WkOZrRewCVZqByUBFswLEA/s1600/main_entry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimhvSV9s7E_Ce1YizPEOzuVGmIQDuoKjoSd42tqMIU0rJBbJBwpUXWK7F08vCC5xK8w0w4aEU15yz_un3fDHXhdXLBFY4YkVQNfc8HXVrp4vEY11GFlOhj5WkOZrRewCVZqByUBFswLEA/s320/main_entry.png" width="320" /></a></div>
<br />
<br />
Right click on the project and select 'Add Existing' and browse to the SDL2 <span style="font-family: "courier new" , "courier" , monospace;">src</span> folder belonging to the SDL2 project. Here there is a folder named <span style="font-family: "courier new" , "courier" , monospace;">main</span>. Inside this is a folder named <span style="font-family: "courier new" , "courier" , monospace;">android</span> which contains a file named <span style="font-family: "courier new" , "courier" , monospace;">SDL_android_main.c</span>. Add this to the project. SDL uses this to implement its own entrypoint and is needed here because it is called automatically from the java application during <span style="font-family: "courier new" , "courier" , monospace;">onCreate()</span>.<br />
To make sure the code is referenced properly from the java application, right click on the java application project's references node in the solution explorer and select 'Add Reference'. Check the box next to <i>main</i> - SDL2 should already be checked, if not check it too. If your project is named anything other than <span style="font-family: "courier new" , "courier" , monospace;">main</span> - or you're adding another library such as <i>SDL_image</i> - browse to <span style="font-family: "courier new" , "courier" , monospace;">src/org/libsdl/app</span> in the solution explorer and open <span style="font-family: "courier new" , "courier" , monospace;">SDLActivity.java</span>. Around line 72 is an array of library names which will be loaded when the application is run. By default it already contains SDL2 and <span style="font-family: "courier new" , "courier" , monospace;">main</span>, but this will need to be modified to add any extra libraries, or if <span style="font-family: "courier new" , "courier" , monospace;">main</span> is named something else. Note that these are the names of the .so files without the preceeding lib and .so extension.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVgtyAGuT_wbdxDkgJsxQk4wVSf2ezE34_onP0PcfN-BVOybzK5kQpQnmIwlqkRAB2SyS2vsEpETzMg17Q4vwTlyquZr3fCpE31Wu9IX7RpOrmXwIzau3MqdFR-7i072_RagCvewgDEuo/s1600/lib_strings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="138" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVgtyAGuT_wbdxDkgJsxQk4wVSf2ezE34_onP0PcfN-BVOybzK5kQpQnmIwlqkRAB2SyS2vsEpETzMg17Q4vwTlyquZr3fCpE31Wu9IX7RpOrmXwIzau3MqdFR-7i072_RagCvewgDEuo/s320/lib_strings.png" width="320" /></a></div>
<br />
<br />
<b>Conclusion</b><br />
This is the bare essential setup for creating an Android compatible application based on SDL2. Adding further libraries can be achieved the same way as setting up SDL2 itself; by creating a new project, configuring it and adding the sourcecode. Remember to reference these libraries in the java application project, and modify the array of library names to include the new library.<br />
<br />
If you wish to use C++ in any project code make sure to set the language version to <span style="font-family: "courier new" , "courier" , monospace;">C++11</span> or <span style="font-family: "courier new" , "courier" , monospace;">C++1y</span> under the language setting of the project properties, should you need any particular language features. <br />
<br />
For reference I've uploaded the configured solution with an example <span style="font-family: "courier new" , "courier" , monospace;">main</span> project to github, which can be found <a href="https://github.com/fallahn/sdl2vs">here</a>. It was made with Visual Studio 2017.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com11tag:blogger.com,1999:blog-6421765335659381070.post-82486715785187855752017-02-26T11:38:00.000+00:002017-02-26T11:38:04.474+00:00DoodleBob Featured on PC GamerBig thanks goes to Jamz who, this week, managed to expose <a href="https://fallahn.itch.io/doodlebob">Bob</a> (as much as a naked doodle could be exposed...) to PC Gamer's <a href="http://www.pcgamer.com/free-games-of-the-week/">free games of the week</a> column. The 15 minutes of fame have shot to Bob's head of course, who is now fervently vacuuming his house in expectation of an influx of new visitors...<br />
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/1AMmD22Osdg" width="560"></iframe>
<br />
On a more serious note the boost to my ego has been palpable, so much so that it looks like the originally intended mobile version will go ahead after all. There is some initial groundwork to be done first, for one thing I still haven't even decided between using Unreal Engine 4 or Unity, as well as giving the graphics an overhaul, particularly some of the 3D models. <a href="http://steamcommunity.com/id/hunter-killer/myworkshopfiles/">Josh's modelling</a> is great, and I'd like to get more of that in there. This also means there's a good chance DoodleBob will make his way to Apple platforms too. For now you can download the windows version from <a href="https://fallahn.itch.io/doodlebob">itch.io</a> or checkout the <a href="https://github.com/fallahn/xyExperimental/tree/master/DoodleChum">source</a> if you want to build it on linux.<br />
<br />
Stay tuned...Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-49460627429554024162017-02-18T02:00:00.000+00:002017-02-18T02:00:21.461+00:00Fangirling so hard right now.<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/fallahn/tmxlite"><img alt="https://github.com/fallahn/tmxlite" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmCFmWyjUj7iJmebapBnjY9yQv0Ar7qzjyoltmOnrEtNlQH_-i_iirF3t3Vkyvc975XaglGjQe4gp1UgnjGRwfORQtKcXfq3C0B5nqY88VAjtpd2Xznzn8QziQTVnt20W6KXeEfQn-Vak/s1600/tmxlite.png" title="tmxlite C++ Tiled map parser" /></a></div>
<br />Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-12976917315195328282017-02-12T13:35:00.000+00:002017-02-12T13:35:19.805+00:00DoodleBob - xygine game released on itch.ioSomehow I've managed to not post about this project whatsoever in the last few months I've been working on it. DoodleBob is a virtual pet type game which I worked on over the christmas holidays, using completely hand drawn artwork - hence the name DoodleBob.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/1AMmD22Osdg" width="560"></iframe>
<br />
<br />
The game is built on <a href="https://github.com/fallahn/xygine">xygine</a> and is <a href="https://github.com/fallahn/xyExperimental/tree/master/DoodleChum">open source</a> for anyone who wants to dig around - or if you just want to try it the game is available for windows (linux users will have to build from source) on the <a href="https://fallahn.itch.io/doodlebob">itch.io page</a>.<br />
<br />
<br />
<iframe frameborder="0" height="167" src="https://itch.io/embed/112645?linkback=true&link_color=177d75" width="552"></iframe>
<br />
I admit there are a few places which could do with some polish - but overall I'm pretty pleased with it. Perhaps at some point I'll write some posts about how the path finding and decision making work. For now though I'm happy to hear any suggestions or opinions through the comments below.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-48820265747182423532017-01-19T19:00:00.000+00:002017-01-19T19:00:22.392+00:00tmxlite - 1.0.1 ReleasedI've released a bugfix update to tmxlite, my lightweight Tiled/tmx parser for C++.<br />
<br />
Fixes include:<br />
<ul>
<li>Fixed typo in FindTMXLITE file</li>
<li>Made render order property optional for backwards compatibility with older map versions</li>
<li>Fixed colourFromString() not properly removing preceeding #</li>
<li>Fixed Parsing of image property of collection of images tilesets</li>
<li>Fixed missing image size property for collection of images tilesets</li>
</ul>
<br />
There are also a couple of new / update features:<br />
<ul>
<li>Added an OpenGL example using SDL2</li>
<li>Moved the SFML example to its own project folder</li>
<li>Updated the CMake file to allow compiling libtmxlite as a dependency of another project</li>
</ul>
<br />
The latest relase has been tested to work on Windows, linux and OS X. Thanks, as ever, go to the community members who have contributed via the <a href="https://github.com/fallahn/tmxlite">Github</a> page.<br />
<br />
You can grab the source <a href="https://github.com/fallahn/tmxlite">here</a> and Visual Studio 2015 builds <a href="https://github.com/fallahn/tmxlite/releases">here</a>.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-9498413933725835812016-11-24T11:31:00.000+00:002016-11-24T11:31:02.487+00:00Procedural 2D Terrain - Part 4: Making Some NoiseSo far I've not covered precise noise generation techniques I used for the terrain, mostly because I've not really settled on any one 'perfect' technique. I have finally begun to refine it, however, particularly as I'm now adding detail textures to the generation so I thought I'd share what I have so far.<br />
<br />
<b>A Quick Recap</b> <br />
If you've not seen how I'm currently rendering the terrain, it's probably worth reading <a href="http://trederia.blogspot.co.uk/2016/11/procedural-2d-terrain-part-1.html" target="_blank">part 1</a> of this series first, as I'll be referring to some of the rendering methods used. I'm currently using a noise generation library named <a href="https://github.com/Auburns/FastNoiseSIMD" target="_blank">FastNoiseSIMD</a> which is highly optimised for x86/x64 processors, as well as being wonderfullly lightweight, requiring only a few source files to be added to the project. My old 2.1Ghz Core2Duo chomps through chunk generation with no problems at all as a testiment to the quality of the library.<br />
Noise generation for each chunk is performed in a series of steps using a variety noise patterns. I'll cover the settings of each as they currently are at the time of writing - although the source code may well have changed significantly by the time anyone reads this. I shall also point out that there is currently no generation of rivers or roads (although I plan to work on at least roads at some point), just everlasting terrain interspersed by large bodies of water. If you're on Windows and using FastNoise there is a binary in the <a href="https://github.com/Auburns/FastNoiseSIMD/releases" target="_blank">releases section</a> of the github repository, which allows previewing the different noise types. This has been incredibly useful while testing different noise parameters for quick visual feedback. While the FastNoise library itself is cross platform (at least, I have it working in xubuntu) the preview executable appears to be Windows only unfortunately, due to its dependence on .net. Perhaps if you're sadistic enough you might get it running with Mono...<br />
<br />
<b>The Data Structure</b> <br />
The underlying data array of each chunk has been modified since I wrote part 1. In that part I stated I use a 64x64 array of <span style="font-family: "courier new" , "courier" , monospace;">std::uint16_t</span>, each value holding a bitmask of parameters which are sent to the shader. I've now increased the data size to <span style="font-family: "courier new" , "courier" , monospace;">std::uint32_t</span>, so that more information my be included per value (each value represents a tile within the chunk). I could probably change the texture type used to store the array to RGBA 8-bit so that each channel is accessable in the shader without having to use bitwise operations to extract the data GPU side, and I may still do that, but for now it remains a single unsigned red channel. The format of each value looks like this (where F represents 4 used bits):<br />
<ul>
<li>0x000000FF - Tile ID. This is used by the shader to look up the index of a tile.</li>
<li>0x00000F00 - BiomeID. There are 9 biomes, which fits nicely in 4 bits</li>
<li>0x0000F000 - Depth value. This isn't the same as the height value, rather it is used by the shader to darken tiles to give some illusion of 'depth'. This is most noticeable in the water.</li>
<li>0x00FF0000 - ID of a detail tile. Details are drawn on top of existing tiles, and exist within each biome tile set, so that each biome has a unique set of details</li>
</ul>
The top byte is not currently used, but will probably be involved in rendering the road systems.<br />
<br />
<b>Creating Tile Indices</b><br />
To calculate the tile ID I use two maps. The first is a simplex fractal set, using the default FastNoise settings. The value is normalised then multiplied by 4 to provide 4 variations of tiles, where 0 is water and 1-3 are land varieties. This creates a 'lumpy' lanscape with pools of water and patches of hills. Then I set the the frequency of the noise generator to a much lower 0.003 to create a simplex set (note no fractals). This is used to create large open areas of water, such as seas and oceans by adding it to the first map before it has been normalised. The low frequency means that large areas of 0 value IDs are created during normalisation, which are interpreted as large bodies of water. It also produces nice looking coast lines, and islands in shallower areas.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOAWYWPuyeyxjCCFYEP_RgWjDmoxbr_4tNwwTjg_I2lER2emlVeOuYyV4PSxdpsJ8MsD4xbbL7Umfvsn_NKfqK0Rpgs3RmFlvEV2nFh7_sMr6hnm-u8Gn4V3h19NUoZix3zpCmc_e2mjU/s1600/terrain_map.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOAWYWPuyeyxjCCFYEP_RgWjDmoxbr_4tNwwTjg_I2lER2emlVeOuYyV4PSxdpsJ8MsD4xbbL7Umfvsn_NKfqK0Rpgs3RmFlvEV2nFh7_sMr6hnm-u8Gn4V3h19NUoZix3zpCmc_e2mjU/s320/terrain_map.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Potential real estate for a harbour?</td><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
These two maps are actually produced one tile larger in width and height than the chunk size - 65x65 values, starting at the position of the current chunk. This is so that once the values are normalised and converted to the 0-3 range they can be passed through the marching squares algorithm I talked about in the last post. Each tile needs to consider its neighbour, which is why the noise sets have the extra values. Once the final tile ID has been calculated it is OR'd into the bottom byte of the chunk's data array.<br />
<br />
<b>Biome Generation</b><br />
To calculate the biome ID I use three noise maps:<br />
<ul>
<li>Rainfall, which is a simplex fractal set with 3 octaves and a frequency of 0.005</li>
<li>Average temperature - a gradient fractal set with a RidgedMulti fractal type</li>
<li>Height data, created from a value fractal set with 2 octaves and a frequency of 0.002</li>
</ul>
I also create a 'latitudinal variance' which is a linear gradient in the Y direction which decreases with distance. This is used to decrease the average temperature the further north or south the chunk appears, and also slightly increase the rainfall value. The height map is also applied to the rainfall and temperature maps, decreasing temperature linearly with height, and reducing the average rainfall to near zero after a certain limit. The rainfall and temperture values are then used as x/y coordinates in the biome lookup table, detailed in the <a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-3-texturing.html" target="_blank">last post</a>, to find the biome ID. This is then shifted and OR'd into the lower half of the second byte of the chunk data array.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3kWIifC2GQaMyTlqos98IPLrBpVb3ZdyPn0_nPQ7EIBLHJBAdhvsy3EiQC3empAzMPydF8dAKjS3MtRZghRpufS00FjhssuPdeOd7w_NUnQMlHYn1GiXmqKKAr5Tk-RO65K660QDhqMI/s1600/biome_map.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="163" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3kWIifC2GQaMyTlqos98IPLrBpVb3ZdyPn0_nPQ7EIBLHJBAdhvsy3EiQC3empAzMPydF8dAKjS3MtRZghRpufS00FjhssuPdeOd7w_NUnQMlHYn1GiXmqKKAr5Tk-RO65K660QDhqMI/s320/biome_map.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Biomes by the sea...</td><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
<br />
Depth data is also calculated at this point, by taking the terrain data map, normalising it, and multiplying it by 15, to fit it in to the top half of the second byte.<br />
<br />
<b>Adding Detail</b><br />
Finally the values of detail tiles are calculated, although not with a noise map. Details, in this case, are tiles which may contain non-interactive vegetation, stones/rocks or items floating in the water, and are scattered as evenly as possible. To get a good distrubution I use a poisson disc function (based on <a href="http://devmag.org.za/2009/05/03/poisson-disk-sampling/" target="_blank">this article</a>, and added to <a href="https://github.com/fallahn/xygine" target="_blank">xygine</a>) which scatters the locations nicely. The side effect of not being noise based is that the result of the function is non-deterministic and varies every time it is run as it is not seed based - but for non-interactive details such as these I don't regard it as a real problem, particularly as chunks are written to disk after generation anyway, rendering the positions fixed. Details are based on the depth value of the current tile, of which the deepest value over open water is ignored. The remaining values are divided by 4 to determine which of the four tile types the current tile is a member of, before then being randomised between one of the 3 variations of detail for that type. These are stored in order in the biome tile set texture starting at index 90: 0A, 0B, 0C, 1A, 1B, 1C... and so on, where the number is the tile type, and letter the detail variation.<br />
<br />
That pretty much sums up the noise generation as it stands - there is still a lot to do including laying out roads and adding cities and other interesting places for the player to visit and explore, but I am pleased with the progress so far.<br />
<br />
<a href="https://github.com/fallahn/xyRacer/tree/master/SuperTerrain" target="_blank">Terrain Generation Repository</a>.<br />
<br />
<u>Previous Parts</u>:<br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-1.html" target="_blank">Part 1</a><br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-2-creating.html" target="_blank">Part 2</a><br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-3-texturing.html" target="_blank">Part 3</a> Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-83429891786965149442016-11-17T18:30:00.000+00:002016-11-24T17:07:32.382+00:00Procedural 2D Terrain - Part 3: TexturingContinuing from <a href="http://trederia.blogspot.co.uk/2016/11/procedural-2d-terrain-part-2-creating.html" target="_blank">part 2</a> of the blog series, it was time to start texturing the terrain.<br />
<br />
<b>The Technique</b><br />
At this point the terrain was rendered by reading an index value stored in an integer texture and using it to choose a colour to be displayed. I modified the shader, based on <a href="http://www.the2dgame.com/index?page=articles&ref=ART8" target="_blank">this</a> article, to look up the corresponding fragment in a tile set, and use the result to colour the current tile. Temporarily I used a texture which looked like this to see which indices were being rendered (warning crude programmer art ahead!):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikzISUU3DOXzB9VkuC0TEmwNe4EJR10E8GVWfdSQuAVBoVloNJNZ9MJpxyJUiNikWkKC1h7LNPQ4KKZlvZkqmAg2c1r0uQUzTUPOJhzzrjorQgOGV-BTEgaZawA582aKunzMQBZ0QVCL4/s1600/test_set02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikzISUU3DOXzB9VkuC0TEmwNe4EJR10E8GVWfdSQuAVBoVloNJNZ9MJpxyJUiNikWkKC1h7LNPQ4KKZlvZkqmAg2c1r0uQUzTUPOJhzzrjorQgOGV-BTEgaZawA582aKunzMQBZ0QVCL4/s320/test_set02.png" width="320" /></a></div>
<br />
While this went mostly without trouble I did discover that on some AMD cards there were occasional rounding errors, which led to the wrong tile being rendered. I fixed this by adding a small offset to coordinate values before rounding (which can be seen as the variable 'epsilon' in the shader source).<br />
<br />
<b>Smooth Transitions</b><br />
The reason the above tile set has 120 tiles is because of what I had planned for the next part; now that I knew any tile could be rendered from within a set simply by using its index I wanted to create smooth transitions between the different textures in the set, rather than the harsh square edges of the tiles. I'd come across<a href="http://blog.project-retrograde.com/2013/05/marching-squares/" target="_blank"> this article</a> on using the <a href="https://en.wikipedia.org/wiki/Marching_squares" target="_blank">marching squares</a> algorithm to process the edges of transitions, so I implented it within the <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class's tile creation function. First the noise values are generated via FastNoise as before - but then the final tile indices are calculated using the marching squares algorithm. Using the above tile set again, I could check the correct tiles were being displayed. As you can see there was a minor problem:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXGhafIyDdzFf6iyWtPJRYN1dMGTJ7qd3AEtMjwhbM5ojIsOd909OoqN5rnGG5CXSYsK567Lq8HEgqWIGMy_wD-NNNKjmpy07vs2_iAKHixTBTTW60x497zaQeVYdgRfyDZGTo_Mzbi5g/s1600/no_60.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXGhafIyDdzFf6iyWtPJRYN1dMGTJ7qd3AEtMjwhbM5ojIsOd909OoqN5rnGG5CXSYsK567Lq8HEgqWIGMy_wD-NNNKjmpy07vs2_iAKHixTBTTW60x497zaQeVYdgRfyDZGTo_Mzbi5g/s320/no_60.png" width="320" /></a></div>
<br />
Tile number 60 would not render...<br />
<br />
After some amount of head scratching, and a lot of probing with <a href="https://github.com/baldurk/renderdoc" target="_blank">renderdoc</a> (thanks Aster!) I discovered that once again this was a rounding error - a position value which should have been 0 was in fact 1, causing tile 60 to render the last column of fragments from tile 74. A quick fix was to enable wrapping on the tile set texture so that the incorrect coordinate wrapped back around to tile 60 - but of course the proper fix was to correct the rounding error with the epsilon value once again. With a hastily scribbled tile set created in <a href="http://pyxeledit.com/" target="_blank">Pyxel Edit</a> (I love this program) testing the terrain now displayed pleasing terrain transitions.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/6KU6tqy7ups" width="560"></iframe>
<br />
<br />
<br />
<b>Texturing Biomes</b> <br />
At the end of the last post I wrote about how I included generating biome data. While the output is still far from refined the terrain was, by this point, generating biome IDs which were stored in the second byte of the array value, and therefore accessible to the shader. Using the ID my plan was to use a GL_TEXTURE_2D_ARRAY containing a tile set for each biome, and use the biome ID in the shader to select the correct texture when rendering. The index in the first byte of the lookup value would then select the correct tile from within the tile set.<br />
<br />
More programmer art was needed to test this - I wanted a tile set (based on the above template) for each biome, but each tileset also needed to be different enough that it was visibly outstanding when rendered. The biome IDs are based on <a href="https://commons.wikimedia.org/wiki/File:PrecipitationTempBiomes.jpg" target="_blank">Whittaker's biome graph</a> so using <searchengine> image search I found some reference images of real-world biomes and, coupled with <a href="http://paletton.com/" target="_blank">Paletton</a>, created a set of palettes which represented each biome.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb6nFX-m9wc9-fNTOCJKUtGVvPNKdNyN2KCBf5miq0MKdvh5n2XuegPvyPYmXOKQ19M_7mDj21U2Olq5caaEMoJBy-j6wTPjlcN9qiW4VGtOkaexZYpPXoPAYAEKWmPR8bjs-JFNMJGaQ/s1600/grouped.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb6nFX-m9wc9-fNTOCJKUtGVvPNKdNyN2KCBf5miq0MKdvh5n2XuegPvyPYmXOKQ19M_7mDj21U2Olq5caaEMoJBy-j6wTPjlcN9qiW4VGtOkaexZYpPXoPAYAEKWmPR8bjs-JFNMJGaQ/s320/grouped.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Palettes for Cold Desert, Shrubland, Boreal Forest, Savanna Forest and Seasonal Forest</td><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
Eventually I'll use these palettes to make a series of complete tile sets, but for now they are enough to make coloured templates. Implementing the rendering didn't go entirely smoothly, however, as although it is possible to modify the OpenGL properties of SFML textures, it is not possible to use any format other than GL_TEXTURE_2D. This was a shame and, although I could have reimplemented the entire rendering setup in OpenGL, meant that I had to reconsider my options. Eventually I settled on building a texture atlas at run time, combining all of the tile sets in to a single texture. The biome ID was still valid, but in the shader was now used to calculate a position within the atlas at which the tile set started, rather than act as an index into a texture array. To combine the textures I took advantage of the <a href="http://www.sfml-dev.org/documentation/2.4.1/classsf_1_1Image.php" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">sf::Image</span></a> class, as not only could I validate that all the textures were the correct size, I could also compensate for load failures by replacing the images with a solid colour. The final atlas texture would still be valid if images on the disk weren't - although would not render correctly of course.<br />
<br />
<span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;">void TerrainComponent::loadTerrainTexture()<br />{<br /> m_terrainTexture.create(width * texturesPerSide, height * texturesPerSide);<br /> for (auto i = 0u; i < biomeCount; ++i)<br /> {<br /> sf::Image img;<br /> if (!img.loadFromFile("assets/images/tiles/" + std::to_string(i) + ".png"))<br /> {<br /> img.create(width, height, sf::Color::Magenta);<br /> }<br /> if (img.getSize().x != width || img.getSize().y != height)<br /> {<br /> img.create(width, height, sf::Color::Magenta);<br /> xy::Logger::log("Image " + std::to_string(i) + " was not correct size.", xy::Logger::Type::Warning);<br /> }<br /><br /> auto xPos = i % texturesPerSide;<br /> auto yPos = i / texturesPerSide;<br /> m_terrainTexture.update(img, xPos * width, yPos * height);<br /> }<br /><br /> m_terrainShader.setUniform("u_tileTexture", m_terrainTexture);<br />}</span></span><br />
<br />
and the fragment shader:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">void main()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> uint value = texture(u_lookupTexture, v_texCoord)<span class="preprocessor">.r</span><span class="comment">;</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> float index = float(value & <span class="number">0xFFu</span>)<span class="comment">;</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> vec2 tilesetCount = u_tilesetCount * biomeCount<span class="comment">;</span></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><span class="comment"> </span>
vec2 position = vec2(mod(index, u_tilesetCount<span class="preprocessor">.x</span>), floor((index / u_tilesetCount<span class="preprocessor">.x</span>) + epsilon)) / tilesetCount<span class="comment">;</span></span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><span class="comment"> </span>
float biomeID = float((value & <span class="number">0xf00u</span>) >> <span class="number">8</span>u)<span class="comment">;</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> vec2 biomePosition = vec2(mod(biomeID, biomeCount<span class="preprocessor">.x</span>), floor(biomeID / biomeCount<span class="preprocessor">.x</span>)) / biomeCount<span class="comment">;</span> </span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> vec2 texelSize = vec2(<span class="number">1.0</span>) / textureSize(u_lookupTexture, <span class="number">0</span>)<span class="comment">;</span></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"><span class="comment"> </span>
vec2 offset = mod(v_texCoord, texelSize)<span class="comment">;</span>
vec2 ratio = offset / texelSize<span class="comment">;</span>
offset = ratio * (<span class="number">1.0</span> / u_tileSize)<span class="comment">;</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> offset *= u_tileSize / tilesetCount<span class="comment">;</span> </span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> colour = texture(u_tileTexture, biomePosition + position + offset)<span class="comment">;</span> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">} </span><br />
<br />
Things are (rather colourfully) now coming together.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSIM4-fbqcR7tVCEtq5SCsCG3Hlkcaw0hjsI4-wfFRuvxypRMFpBq5WM5hyUW4V8VA5j-JY52d-Lzpu2f3ybaKFIO7BhltXLxmKerMw27QUs-aYju3yT0A4RIStkPdFGyw49RgQYiJULQ/s1600/screenshot07_11_16_13_45_36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSIM4-fbqcR7tVCEtq5SCsCG3Hlkcaw0hjsI4-wfFRuvxypRMFpBq5WM5hyUW4V8VA5j-JY52d-Lzpu2f3ybaKFIO7BhltXLxmKerMw27QUs-aYju3yT0A4RIStkPdFGyw49RgQYiJULQ/s320/screenshot07_11_16_13_45_36.png" width="320" /></a></div>
<br />
After I've finished creating more detailed tile sets it will be time to tackle terrain details, such as foliage, as well as further refining the noise generation technique. For now though the full code can be found in the <a href="https://github.com/fallahn/xyRacer/tree/master/SuperTerrain" target="_blank">repository</a>.<br />
<br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-1.html" target="_blank">Part 1</a><br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-2-creating.html" target="_blank">Part 2</a><br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-4-making.html">Part 4</a> Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-19542972523653382182016-11-15T19:00:00.000+00:002016-11-15T19:00:01.795+00:00tmxlite - A lightweight Tiled map parser for C++14One of the biggest problems with the <a href="https://github.com/fallahn/sfml-tmxloader" target="_blank">SFML tmx map</a> loader is its dependency on not only <a href="http://www.sfml-dev.org/" target="_blank">SFML</a> but <a href="http://www.zlib.net/" target="_blank">zlib</a>, which can be a pain to set up correctly on many platforms. A while back I stumbled upon <a href="https://github.com/richgel999/miniz" target="_blank">miniz</a> which is a drop in replacement for zlib in a single source file. I used it when adding <a href="http://trederia.blogspot.co.uk/2016/08/tiled-map-support-in-xygine.html" target="_blank">tmx map support</a> to <a href="https://github.com/fallahn/xygine" target="_blank">xygine</a>, and was so pleased with the result that I've spun off the parser in to its own library. <a href="https://github.com/fallahn/tmxlite" target="_blank">tmxlite</a> is small, supports all compression formats, and can be compiled in to a static or shared library on any platform which supports CMake (probably even on mobile!), without any of the fuss of linking to external libraries. It also offers (in my opinion) a far cleaner API to the map structure of <a href="http://www.mapeditor.org/" target="_blank">Tiled</a> maps than the SFML map loader, so that any graphics library can be used with it, not just SFML. If you're intrigued, or just want to try a different library, you can find it on Github <a href="https://github.com/fallahn/tmxlite" target="_blank">here</a>.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-14651433713796192542016-11-10T18:30:00.000+00:002016-11-18T14:38:32.950+00:00Procedural 2D Terrain - Part 2: Creating ChunksIn my <a href="http://trederia.blogspot.co.uk/2016/11/procedural-2d-terrain-part-1.html" target="_blank">last post</a> I wrote about how I set up rendering of chunk data in the <a href="https://github.com/fallahn/xyRacer/blob/master/SuperTerrain/src/TerrainComponent.cpp" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span></a> class of my <a href="https://github.com/fallahn/xyRacer/tree/master/SuperTerrain" target="_blank">procedural terrain experiment</a>. Once I had this working it was time to start creating chunks on the fly, and then caching the data to be used when a chunk was revisited.<br />
<br />
<b>Resource Management</b><br />
To handle the generation of chunk data the <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> is put in charge of ownership of the <a href="https://github.com/fallahn/xyRacer/blob/master/SuperTerrain/src/Chunk.cpp" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">Chunk</span></a> class instances, as well as any resources they require. When creating and destroying objects frequently memory fragmentation can be a concern, and repeatedly loading / saving assets from storage can also bottleneck performance. Chunks are allocated on the heap using <a href="http://en.cppreference.com/w/cpp/memory/unique_ptr" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">std::unique_ptr</span></a>, so to address the first issue <a href="https://github.com/fallahn/xygine" target="_blank">xygine</a> (my game framework within which I'm currently experimenting) provides an <a href="https://github.com/fallahn/xygine/blob/master/xygine/include/xygine/detail/ObjectPool.hpp" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">ObjectPool</span></a> class, designed to manage allocated memory and efficiently recycle it, preventing any fragmentation. Depending on where the player is in the world up to 4 chunks are visible at one time, and generally 9 chunks are active at any time (the current chunk containing the player and 8 surrounding chunks) so a fixed allotment of memory can be used, from which the <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> can draw upon as it creates new chunks. When chunks are destroyed the memory is marked as free and automatically reallocated by the <span style="font-family: "courier new" , "courier" , monospace;">ObjectPool</span>.<br />
Chunks also require at least one shader when being drawn (other shaders may be used for effects like water) and continually creating / destroying shaders is time consuming. The <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> has a single shader as a member, compiled on start up, a reference to which is passed to each new chunk as it is created. As outlined in <a href="http://trederia.blogspot.co.uk/2016/11/procedural-2d-terrain-part-1.html" target="_blank">part 1</a> each chunk requires a specially formatted texture which is used as a lookup table and, again, to keep creating and destroying these would be both time consuming and quite possibly cause memory fragmentation. The <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> therefore manages a pool of textures so that they may be reused rather than recreated. This is done by creating a <span style="font-family: "courier new" , "courier" , monospace;">ChunkTexture</span> alias<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">using ChunkTexture = std::pair<sf::Texture, bool>;</span><br />
<br />
and creating a vector of <span style="font-family: "courier new" , "courier" , monospace;">ChunkTexture</span>. Textures are paired with a bool flag which marks whether or not the texture is currently in use by a chunk. The vector has to be initialised in the <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> constructor so that the textures are in the correct format and the flags have an initial value of false.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">for (auto& tp : m_texturePool)<br />{<br /> tp.first.create(64, 64);<br /> glBindTexture(GL_TEXTURE_2D, tp.first.getNativeHandle());<br /> glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, tp.first.getSize().x, tp.first.getSize().y, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, 0);<br /> glBindTexture(GL_TEXTURE_2D, 0);<br /> tp.second = false; //texture not yet used<br />}</span><br />
<br />
As <span style="font-family: "courier new" , "courier" , monospace;">m_texturePool</span> is a <span style="font-family: "courier new" , "courier" , monospace;">std::vector</span> a utility function can quickly find the first unused texture:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">ChunkTexture& TerrainComponent::getTexture()<br />{<br /> return *std::find_if(std::begin(m_texturePool), std::end(m_texturePool), <br /> [](const ChunkTexture& ct)<br /> {<br /> return !ct.second;<br /> });<br />}</span><br />
<br />
A reference to a free texture is then passed to a <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span>, along with the shader, when it is created (I'm aware of the assumption that the above function will always return a free texture - this is because the <span style="font-family: "courier new" , "courier" , monospace;">ObjectPool</span> for the <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> instances is a fixed size, which is the same size as the texture pool. If, at any point, I'm trying to create more chunks than I have free resources then I've got a bigger problem on my hands...). This takes care of the resource handling, but the <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> has a second job: to monitor the player's position in the world, and update the corresponding world chunks.<br />
<br />
<b>Creating Chunks On The Fly</b><br />
Two things needed to be done to the <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class then. Firstly a chunk needs to have some positional data. It needs a position in world space, and a size. This is done by providing an <a href="http://www.sfml-dev.org/documentation/2.4.0/classsf_1_1Rect.php" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">sf::FloatRect</span></a> which represents the global bounds of the chunk. The bounding rectangle can be used to test if it contains the player position, and compare it to the position from the last frame. If the current chunk differs from the previous chunk then the player has moved from one chunk to another, triggering a chunk update. Each active chunk is tested, via its global bounds, to see if it contains one of 8 points surrounding the current chunk (which contains the player). If the test fails the chunk is removed, the <span style="font-family: "courier new" , "courier" , monospace;">ObjectPool</span> automatically freeing the memory, and the destructor of the chunk marks its texture as being free. The surrounding points are again tested to see which chunks do not yet exist, and any missing chunks are created in the active chunks list.<br />
Secondly a chunk has to be able to update its texture with an array of tile data. This is done by giving each chunk a unique ID based on a hash of its 2D position. When a chunk is created it looks to local storage for a file with the same name as its ID. If it is found the file is loaded and the contents copied to the chunk array. As the data is only 8kb in size I've found that this loads fast enough to not visibly block the execution of the program - although I have yet to implement any error checking. If a matching file is not found, however, then the chunk must be generated from scratch using the noise library. This, I have found, did cause some visual stutter, so the chunk creation function is called in its own thread. Some care is required for syncronisation, but an atomic bool flag is enough to tell the chunk when the data is ready to be uploaded to its texture. The chunk's position is needed here too as the noise function which generates terrain data does so based on a coordinate system. Once new chunk data has been created it is saved immediately to a file, so it can be reloaded next time the chunk is visited.<br />
<br />
<b>The Result</b><br />
By this point I had a smooth, seemingly endless terrain generating on screen, with nice, tightly managed resources. I was able to leave a weight on the arrow keys of my keyboard for about 20 minutes so the 'player' kept on walking in a single direction, with absolutely no problems at all (don't worry the video isn't 20 minutes long...).<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/hNONj_Ru3xo" width="560"></iframe>
<br />
<br />
Now that the chunk system was working it was time to start working on generating biome data. Biomes, as I am sure many people are familiar with, describe the differences in terrain, such as forest areas, deserts or mountains. Using <a href="http://www.gamasutra.com/blogs/JoshNewland/20150624/246897/2D_procedurally_generated_world_building_in_Unity.php" target="_blank">this post</a> as a guide I set about generating further noise maps when a chunk was created for the first time, which create data that can be used to look up a biome ID from a static table of data. This ID is then OR'd into the data array value at that coordinate. This will eventually be used to select the correct texture for that biome, although currently I feel the noise generation part could do with some tweaking. In the above video the middle view of the minimaps on the right displays the different biomes, with which I am currently not personally happy. Some fine tuning will be required, before moving on to texturing...<br />
<br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-1.html" target="_blank">Part 1</a><br />
<a href="http://trederia.blogspot.com/2016/11/procedural-2d-terrain-part-3-texturing.html" target="_blank">Part 3</a> Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-83689272594411183552016-11-03T18:30:00.000+00:002016-11-18T14:39:08.527+00:00Procedural 2D Terrain - Part 1: RenderingThere comes a time in every programmer's life when he or she decides to create their own vast, explorable, procedurally generated world. I don't know why, it's as if it's some rite of passage all enthusiastic game programmers must go through. I've been delving in to it myself in recent weeks, and this post is going to attempt to document some of my experiences. Much of what I've done is merely a reimplementation of other works, based on blog posts scattered about the web (and linked where possible), although I like to think I've managed to apply some of my own ideas to the technique.<br />
<br />
In part 1 I'll skip over any particular data generation details. Procedural terrain is generally created via a combination of noise maps, and there are many <a href="http://www.gamasutra.com/blogs/JoshNewland/20150624/246897/2D_procedurally_generated_world_building_in_Unity.php" target="_blank">resources</a> out there covering how height, temperature and rainfall data are combined with landscape details to create a convincing (or at least attractive and interesting to visit) world. Instead I want to outline how I store and manipulate that data once it is accrued, and how it is rendered to screen. Part 2 will cover how the terrain data is split in to chunks and streamed to and from disk. If I ever get around to it part 3 will cover texturing the terrain, including the marching squares algorithm and biome texture selection.<br />
<br />
<b>Setting Up</b><br />
<span style="font-weight: normal;">As always my experiment begins with my framework, <a href="https://github.com/fallahn/xygine" target="_blank">xygine</a>. This takes care of most of the boilerplate code, so I can dive straight into the terrain generation. The terrain itself is constructed around two main classes. The <a href="https://github.com/fallahn/xyRacer/blob/master/SuperTerrain/src/TerrainComponent.cpp" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span></a> which implements xygine's <a href="https://github.com/fallahn/xygine/wiki/Components" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">Component</span></a> interface, so that the terrain can be drawn as any other drawable within a <a href="https://github.com/fallahn/xygine/wiki/Scene" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">Scene</span></a>. The <span style="font-family: "courier new" , "courier" , monospace;">TerrainComponent</span> manages the resources needed to draw the terrain, as well as a series of instances of the <a href="https://github.com/fallahn/xyRacer/blob/master/SuperTerrain/src/Chunk.cpp" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">Chunk</span></a> class. The <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class is used to calculate and draw a section of terrain and is where all the actual data is located. The repository for the terrain experiment is available <a href="https://github.com/fallahn/xyRacer/tree/master/SuperTerrain" target="_blank">on Github</a>, so you can pick the source apart in all its gory detail. The noise maps used to generate the data are most commonly created via perlin or simplex fractal algorithms. There are other <a href="https://spin.atomicobject.com/2015/05/03/infinite-procedurally-generated-world/" target="_blank">resources</a> out there covering the topic, so I'll skip the details beyond stating the requirement of some sort of noise map generation code. As I'm too lazy (and, quite frankly, too ignorant) to write my own, I use a library called <a href="https://github.com/Auburns/FastNoiseSIMD" target="_blank">FastNoise</a>. In general <a href="http://libnoise/" target="_blank">libNoise</a> appears to be the most commonly recommended library, but I've found that a FastNoise variation, FastNoiseSIMD, is lightweight, and very performant. As its name suggests it uses <a href="https://en.wikipedia.org/wiki/SIMD" target="_blank">SIMD</a> optimisations to dramatically speed up the time it takes to generate noise maps, which is important if I want to generate them in real time as the player wanders around the world, but has the drawback of being compatible only with x86/x64 CPUs. It is still cross platform, however, I have it working on both Windows and linux - and, if I gave a fig about Apple, I'm sure it'd work on OS X too.</span><br />
<span style="font-weight: normal;"> </span>
<br />
<b>Data Design</b><br />
At its core the data required to render a terrain is just an array of bytes which represent a grid. In this case, to try and keep the array as small as possible, I use an array of <span style="font-family: "courier new" , "courier" , monospace;">std::uint16_t</span> - unsigned short if you prefer - to store the tile IDs of the texture used to draw a chunk. IDs themsevlves never actually exceed 255 and so could be stored in a single byte, but I also store other metadata per tile. As the terrain is split in to biomes, for example forest, desert or tundra, only the bottom byte is used to store the tile ID - the top byte contains the ID of the biome to which the tile belongs. This means that biomes can be represented with different tile sets - the tile ID itself merely represents a tile within that set. I find this is more flexible as biomes can be added more easily by creating a new texture from a template, as well putting a cap on the maximum tile ID.<br />
A single chunk is made of 64 x 64 tiles, which totals 4096 values. As each tile value is 2 bytes in size the entire array is a mere 8kb in total. This is useful not only because of the small memory footprint, but because at some point I'll want to read and write the chunks to disk and the smaller the amount of data required the faster the operation will be. The array of data is also maintained within the <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class so that, should I chose, the player can interact with and modify the terrain which ultimately comes down to updating one or more tile values. The updated chunk data can then be saved, so player modifications to the world are persistent, and automatically loaded next time the chunk is visited.<br />
<br />
<b>Creating Data</b><br />
To begin with I was only interested in getting something displayed on screen, refining the map generation and calculating biome data would come later, as well as managing multiple chunks of terrain. When drawing tile maps with <a href="http://www.sfml-dev.org/" target="_blank">SFML</a> (the library powering xygine) the most common technique is to use a <a href="http://www.sfml-dev.org/tutorials/2.4/graphics-vertex-array.php#example-tile-map" target="_blank">vertex array</a>. This usually involves some complicated wrangling of vertex data, tile positions, texture coordinates and so on, and also often suffers the <a href="http://en.sfml-dev.org/forums/index.php?topic=16120.0" target="_blank">pixel offset problem</a>. More recently, based on a <a href="http://www.the2dgame.com/index?page=articles&ref=ART8" target="_blank">post</a> by Mickaƫl Pointier (known as the venerable _Dbug_ on #sfml) I implemented GPU side tile mapping for xygine's <a href="https://github.com/fallahn/xygine/blob/master/xygine/include/xygine/components/TileMapLayer.hpp" target="_blank">tmx/Tiled map renderer</a> component, and so decided to extend the technique to the terrain renderer. To first get the map data from the noise generator in to a suitable format I populated the chunk's array, mapping the -1 to 1 floating point values to 0 - 3 integer range. This would represent 4 initial tile IDs which could be drawn on screen. FastNoise generates three dimensional data and it can be slightly confusing retreiving a 2D plane, as it reverse iterates the noise set starting with the z axis.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">float* noiseData = noise->GetSimplexSet(0, 0, 0, 64, 64, 64);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">for(auto z = 0; z < 64; ++z)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> for(auto y = 0; y < 64; ++y)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> std::size_t idx = z * 64 + y;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> std::uint16_t value = static_cast<std::uint16_t>((noiseData[i] * 0.5f + 0.5f) * 3.f);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> m_data[i] = value; </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">}</span><br />
<br />
This is a simplified example of noise creation - it doesn't cover calculation of biome data, but provides enough to get started on visualisation. Notice how the range of the noise value is first normalised, then multiplied by 3 - the maximum tile ID for this example. From here I wanted to move the values in <span style="font-family: "courier new" , "courier" , monospace;">m_data</span> to the GPU so that they can be used as a lookup table. In theory I could pass the array as a uniform to the shader, although in this case I opted to create a texture. Textures are fundamentally an array of data in video memory, so creating a texture from the chunk data means it is only uploaded to VRAM once, until the next time it's updated. SFML provides a nice clean API, hiding away the details of OpenGL, but unfortunately this means that, by default, the texture format is not optimal. SFML expects textures to be 8bit RGBA, whereas the tile map data is 16bit. However SFML does expose the underlying OpenGL texture ID so with a little work I can modify the texture format to suit my needs. In theory I could split the 16bit data in to 2 8bit RG channels (which I may eventually do to reduce the amount of bitwise operations performed) but for now I've taken advantage of the R16UI format.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">texture.create(64, 64);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">glBindTexture(GL_TEXTURE_2D, texture.getNativeHandle());</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI,64, 64, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_data.data());<br />glBindTexture(GL_TEXTURE_2D, 0);</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">The texture is created as normal, but then I use the native handle to bind the texture, and modify its format. <a href="https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">glTexImage2D()</span></a> sets the format of the texture to <span style="font-family: "courier new" , "courier" , monospace;">GL_R16UI</span> (red channel only, 16 bit unsigned integer), its size to the chunk size, and takes a pointer to the array of data created from the noise function as a final parameter. The <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class can update its texture at any time with <a href="https://www.opengl.org/sdk/docs/man/html/glTexSubImage2D.xhtml" target="_blank"><span style="font-family: "courier new" , "courier" , monospace;">glTexSubImage2D()</span></a>, so if the data is modified, say by a player at run time, the modified data can quickly be uploaded to the existing texture. This also proved to be very flexible when splitting the terrain into multiple chunks as a set of textures can be pooled and reused between chunks. As far as the CPU side of things go this was pretty much all that was needed to render a chunk - no vertex arrays, no wrangling of multiple data buffers. Now it was over to the GPU...</span></span><br />
<br />
<b>Displaying the Data</b><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">Drawing the chunk requires a shader to take advantage of the lookup texture, but preliminarily is pretty simple. The draw function of the <span style="font-family: "courier new" , "courier" , monospace;">Chunk</span> class merely binds the texture and the shader, and draws it as a single quad via a vertex array (OK so I still need a vertex array, but 4 fixed points are far easier to manage).</span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">void Chunk::draw(sf::RenderTarget& rt, sf::RenderStates states) const<br />{ </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> m_shader.setUniform("u_texture", m_chunkTexture);<br /> states.texture = &m_chunkTexture;<br /> states.shader = &m_shader;<br /> rt.draw(m_vertices.data(), m_vertices.size(), sf::Quads, states); </span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;">}</span> </span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">It is important to set the states texture as it allows SFML to internally set up the texture coordinates of the vertex array. The vertex array is also sized so that the quad is 4096 x 4096 pixels in size, making each tile 64px square (though this may change at some point). All the grunt work is done in the fragment shader but, as it requires version 1.3 of GLSL, I also had to use a custom vertex shader. The vertex shader can be seen <a href="https://github.com/fallahn/xyRacer/blob/master/SuperTerrain/src/TerrainComponent.cpp#L50" target="_blank">here</a>, and the first revision of the fragment shader is listed below:</span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">#version 130</span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">uniform usampler2D u_texture;</span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">in vec2 v_texCoord;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">in vec4 v_colour;</span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">out vec4 outColour;</span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">vec3[4] colours = vec3[4](vec3(0.0,0.0,1.0), vec3(1.0,1.0,0.0), vec3(0.8, 1.0,0.0), vec3(0.0, 0.9,0.1)); </span><br />
<span style="font-size: small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">void main()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> uint index = texture(u_texture, v_texCoord).r;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;"> outColour = vec4(colours[index], 1.0);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: small;">} </span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">The texture sampler is of type <a href="https://www.opengl.org/wiki/Sampler_(GLSL)" target="_blank">usampler2D</a> - because the lookup texture contains unsigned integer values, as opposed to default floating point type. The shader then takes the value from the current fragment and uses it as an index in to the <span style="font-family: "courier new" , "courier" , monospace;">colours</span> array, to colour the final output. Eventually this lookup will be done within a tile set texture, but for now this is enough to check that it was working. </span></span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUKdklgYOT46-7qcRTpiy43sLr5ug3ZLOXR0H5EqurUB4BF2DCQ5gLV2oVeploNMxe8yQMSfB_1hxHeE0HfL8-vPIGm0N1mKG1-42wS9XDwlBXMuXVMIVRG9n_MqXD2jWNe_WkshylQo/s1600/screenshot03_11_16_13_09_37.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUKdklgYOT46-7qcRTpiy43sLr5ug3ZLOXR0H5EqurUB4BF2DCQ5gLV2oVeploNMxe8yQMSfB_1hxHeE0HfL8-vPIGm0N1mKG1-42wS9XDwlBXMuXVMIVRG9n_MqXD2jWNe_WkshylQo/s640/screenshot03_11_16_13_09_37.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">The
three smaller images on the right are there to help visualise some of
the other data such as biome coverage, which I'll explain more about
that in the next post.</span></span></td></tr>
</tbody></table>
<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;">In summary: the tile information of a terrain chunk can be stored as an array of integer values, representing the ID of a tile. This array is stored in a texture on the GPU which is used by the fragment shader to look up the ID of the tile to be drawn, moving as much of the image processing from the CPU to the GPU as possible. </span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><a href="http://trederia.blogspot.co.uk/2016/11/procedural-2d-terrain-part-2-creating.html" target="_blank">Part 2</a> </span> </span>Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0tag:blogger.com,1999:blog-6421765335659381070.post-59259190201434783892016-09-06T19:30:00.000+01:002016-09-08T14:37:21.384+01:00xygine featured on Push Button, Receive Code and Lunar Mooner updateI happened to discover that the lovely chap over at <a href="http://pushbuttonreceivecode.com/" target="_blank">Push Button, Receive Code</a> has a tutorial series which uses xygine. This makes me really happy that someone else is finding xygine useful to the point that they want to create a good quality tutorial series with it - something I've never managed to do myself. I highly recommend you <a href="http://pushbuttonreceivecode.com/blog/top-down-shoot-em-up-mechanics-part-3" target="_blank">go read</a> the series, which covers making a top-down shooter not unlike classics such as SmashTV. I'd like to point out though that it's 'xygine' with a small 'x'. I don't know why, I'm just odd like that...<br />
<br />
On a xygine related note I've been working more on <a href="http://trederia.blogspot.co.uk/2016/05/introducing-xygine.html" target="_blank">Lunar Mooner</a> (working title), adding <a href="http://trederia.blogspot.co.uk/2016/06/xygine-feature-3d-model-component.html" target="_blank">3D models</a> using xygine's <a href="https://github.com/fallahn/xygine/wiki/Mesh-Renderer" target="_blank">mesh renderer</a>, and a new mini-mode transitioning between levels of the game:<br />
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/dxUoOXfu0RA" width="560"></iframe>
<br />
I have a second mini-game planned although the 'Planet Hopper' mode needs a fair amount of polish before I start on that. As usual you can keep track of the updates (along with all the fresh new bugs...) via the <a href="https://github.com/fallahn/LunarMooner" target="_blank">repository</a>.Matt Styleshttp://www.blogger.com/profile/10214798883054643328noreply@blogger.com0