This summer, I plunged into the depths of my back up drives and came up with
some old projects that were growing some dust. Like most old projects, I find
them, get excited. Decide to do a major revolutionary revamp, and ultimately
just end up touching up some things and kicking them out the door. The
DropFramework is one such thing. For a long time, I wanted to make my own
micro-framework to compete with the likes of Slim or Silex. In the end though, I
really feel that those two have the space of micro-frameworks very well covered.
No one needs yet another PHP micro-framework with half-done ideas floating
around. Still, I did want to show it off, so I polished it up a little bit and
through it up on github. Below are my thoughts on “Yet Another PHP
Microframework”
Yet Another PHP Microframework
For all intensive purposes, you can consider this an abandoned project and I
would not recommend anyone actually use this in production.
A few years ago when Code Igniter was still quite a hot thing and a lot of
servers were still running PHP 5.2, e.g. the “dark ages” before we got all the
nice things that came along in PHP 5.3 it seemed to be quite the fashion for
everyone to try their hand at writing their own framework.
This was my go at it.
You will find a lot of similarities with Code Igniter (since that is the
framework I worked with at the time) and you might also find a lot of classes
that look like they came straight out of PHP Objects, Patterns and
Practice
since that was my bible.
I wanted to do a few things in writing the DropFramework:
I wanted to better understand the MVC pattern, the choices being made and
how CI works.
I wanted a framework that was small enough that I could read and understand
every class in it.
I wanted a framework with a very small footprint that worked by
transforming HTTP requests into request objects / command objects. This
allowed me to fire up multiple instances of the framework per HTTP request
with the master application generating it’s own request objects that it
would feed into it’s child instances and then construct a document out of
the application responses from the children.
I did not like at the time, and still do not like the major design patterns
of a lot of ORM solutions which tend to treat the database as the
authoritative model of the data. I rather turn this convention upside down:
treat the database as just another form of user input. The model can then
be constructed from any form of input – the database, an HTTP post, a
file. The PHP object is then the authoritative source for how the data
structure relates with other data. Any data coming into the model passes
through a validation layer that translates it (or rejects it if it
invalid).
Whether or not I succeeded at this items? I don’t think I would really know.
Version 0.4.0
The version of the framework that had been sitting on my hard disk for some time
was 0.3.0. In deciding to release it I have done two major things:
I created a simple example of the framework working. The
code for this example is
also up on github and a live
version as well.
I namespaced the entire framework and brought it into PSR-4 compliance
allowing for installation via Composer and the use of the Composer
autoloader. This defeats a lot of the purpose of the PHP 5.2 era frameworks
which devoted a lot of their resources to locating and managing the loading
of assets. This, of course, makes this no longer a PHP 5.2 compatible
framework and probably even makes a lot of the framework look rather silly.
Getting started with Piston can be a little daunted right now. Mostly this is
because it’s a project that is still evolving and which has either little
documentation or documentation that rapidly becomes wrong. A lot of games that I
found made with Piston can no longer be compiled, a lot of example code needs
various minor tweaks to get to compile, etc. That said, the two best items that
I found where:
Piston-Mov-Square Which is
just a very simple program that appears better structured than other
examples
Getting Started
The first hurdle in getting Piston to work was getting the SDL2 and GLFW
dependencies installed. Cargo does a great job of going out and grabbing
everything else, but these two items require you to do it yourself. SDL2 was
rather easy and the instructions for it can be found in the Getting Started
tutorial (see above). GLFW was a bit more of a pain and I ended up going through
a stack
overflow
question to get it working. If anything, I would just point to the Getting
started tutorial to get the initial empty repository set up with cargo and all
the dependencies in the Cargo.toml.
My Repository at this Point
At this point my repository looks like
this
I began by setting up a new Piston project as detailed in the Getting Started
tutorial and from there I copied the code from the piston image
example.
This was just a nice starting point to ensure that everything is working and
that the Rust logo would appear in the window designated.
From there, I began working through the Piston-Mov-Square project and the
Getting Started tutorials and religiously commenting every line of the code with
what it does. This is just something I picked up in college and a good way to
puzzle out foreign code. Even if the comment turns out to be wrong (like it
happened in many cases for myself), it at least is a step in manually dealing
with the code.
I played around for a while and after I felt confident in the code that I had, I
began abstracting it into various data objects and getting down to work.
Hopefully my puzzling with help someone else to understand this faster than I.
An Explanation of the Code
Loading Crates
We begin by loading all of our various library provided to us by the Piston
developers and which we will use for getting our game window to appear on the
screen. I have yet to figure out what the #![feature(globs)] lint actually
does and if someone does know, I would love to find out since removing it causes
everything to break. The rest of the code is just giving us access to various
libraries that we will use latter on. I have tried to comment those libraries as
best I could since it wasn’t entirely clear what does what.
Config and Main Entry Point
If there is one thing that I know it’s to confine magic
numbers. Let them
sprout wherever you please and code maintenance becomes a mess. Hence, I have
taken the various constants for our game and packaged them up into a
GameConfig struct. Right now this struct defines the attributes of our window:
title, height, width, frames per second, and tile size. I imagine that this
structure will probably grow larger as we begin adding in actors, players, and
assets. We will deal with that when the time comes.
I have also created a Game struct (more on it later). The game struct simple
takes a GameConfig and returns an instance of itself. Calling run fires off
our game loop which loops infinitely or until we kill the process. In essence
the Game struct represents and handles the loop. We could leave this in main,
but by turning it into a struct we have the option further down the line of
moving it out into a module which would leave our main.rs file consisting only
of loading Piston, setting the various config items and calling Game.run.
The Game Struct
I’ve seen this simply called App, but since we are making a game, I think it
should be Game. The Game simply holds the game state and runs the game loop.
Inside it, I have added several methods via impl: new, run, window, and
render. New and run are our public methods which we have already seen. One takes
a GameConfig and returns a Game. The other starts the game loop. The
remaining methods are just there to run the internals of the loop itself. Let’s
walk through each method:
Game.Run
This one is rather simple. It is a public function (pub fn) named new. We can
access it via Game::new(). It takes a GameConfig and returns a Game whose
config property is config. I am sure I am mixing a lot of OOP language here,
but after years of working in the realm of PHP that’s just how I end up
thinking.
Run is a little messier it fires off our game loop. It takes a mutable copy of
itself which allows us to access it on an instance of Game e.g. game.run().
The first line it calls is to a member function window():
This is not a public function, thus when we turn Game into a module it will not
be accessible outside of the module file. We are using this essentially as a
hidden or private method on Game. The window function is accessible from
inside a game object via self, e.g. self.window(). We really only need one
window, so this method is only called once at the start of the run method.
Window returns a WindowSDL2 which is our back-end we loaded way above at the
start for managing our windows. This window takes a WindowSettings struct
whose values we pull out of the GameConfig stored in our Game. Either way,
it makes a new WindowSDL2 and passes it back to the run method. Now back to
our second line of the run method:
Now this took me a while to figure out. The call to Gl::new() must come
after the creation of the WindowSDL2. In an earlier version of this I had
the call to create GL after the call to create the Window. The code will compile
fine if you create GL first and then the Window, but when you run it you will
get a CreateShader error. I only solved this by stumbling upon an IRC log.
Anyways, hold on to that gl variable since we’ll be passing it around a lot.
Rather boring. We need to create and EventSettings object to pass into our
game loop.
Here is the magic! The game loop. I really like how this works in Rust. Since
iterators can go from 0 to infinite we take advantage of it. The EventIterator
takes the window and event_settings variables we set up earlier and returns
something (I don’t know what) which is put into e. We then do a match on e
to see what was returned. Right now there are only two things that can match: a
call to render the screen, or everything else. Looking at some of the example
code, I do see that we can catch all different kinds of events – user input,
calls to update the game state, etc. but for now we are just concerned with
rendering the screen. So we get a render event (Render(args)) and we call our
private method render via self.render and pass in our gl variable (I said
we would be passing him around a lot).
Game.Render
Render simply takes a mutable reference to Gl and paints to our screen. The
first two lines just get the window_height and window_width out of our
config since we will be using them a lot in this method. Since this is going to
be a tiled game we need to know how many columns and rows of tiles we will be
drawing. So I calculate that here by dividing the window’s height and width by
the tile_size.
The next two lines in our render call do two important things. First we set our
view port to start at the cordinates 0,0 and to extend to the width and height
of our window. Second, we get a Context which I like to think as our virtual
pen for drawing on our canvas. In fact, the first thing we do is fill the entire
canvas with white:
This takes an rgb (red, green, blue) value that sets each to a 100% (or white)
and then draws this to our window by calling draw and passing in our old friend
gl.
Now let’s have some fun. Just to show that we are indeed drawing on the window,
let’s fill the window with 32x32 pixel tiles each one slightly reader than the
last. The effect should look like this:
We begin by setting our starting red value:
This needs to be mutable since we will be adding to it with each iteration of
our rows.
Second, we loop through each row and each column drawing a red square the size
of our tiles:
What does this do? First we are looping through our rows from zero go num_rows
(we calculated the number of rows earlier). On each row we adjust our redness
slightly this should make each row more red than the last with the first row
being fairly dark. Next we calculate row_shift this is simply done my
multiplying what row we are on by the size of our tiles. This will be used to
tell the context to move down 32 pixels when it gets to row 2, and down 64
pixels when it gets to row 3 and so forth. The inner loop does the same only for
our columns. We loop through each column and calculate our col_shift or how
far to shift to the right for each column. If I recall correctly this is the
most efficient way to loop since the screen paints outwards from your upper-left
corner. Finally, we draw our square. The context (c) knows how to draw squares
so we pass into it the coordinates of the upper-left corner of our square
(col_shift, row_shift), the width of our square as a float (tile_size),
instruct the context to fill this square by calling rgb( red, 0.0, 0.0 ).
Note, we passed in our red variable so the redness of the tiles should adjust
as the red variable does. Last, we draw the square by calling draw and once
again passing in gl.
A rather rambling design document for my ideas for a Centipede
clone that I’m releasing under
the MIT license. Following all my reading in Rust it
seems like a good idea to have some kind of project to complete. After
scrounging about for ideas, I came up with the one of doing an open source
centipede clone using Piston. This would be good
practice for trying a Rust Ludum Dare next April.
The following is more or less a rambling stream of consciousness design doc for
what I’m about to do. I’ll probably follow this up with a series of other
entries about the steps and break down of the code as I go.
Concept
A Centipede clone done in Rust using Piston with perhaps some additional flavor.
The core idea of the game is to have a gridded window of size X and Y with a
centipede that begins with one segment that grows as the game progresses. The
centipede moves continuously in the last cardinal direction specified by the
player. As the character moves it encounters various items randomly populated on
the screen. Upon contact some effect occurs such as adding an additional
segment. If the user comes into contact with itself (such as looping back around
on it’s own tail). The game ends or some failure condition occurs.
Objects in the Game
The Game
Well of course it’s an object unto itself. The game represents the game loop.
The Board
The board is 800x480 and divided into 32 pixel squares. At start of the game and
at a fixed interval actors are randomly assigned squares on the board.
Centipede
The centipede has the following characteristics:
Collection of Segments
Who each have a position and sprite
Who each have a direction (Each moves in the direction of the segment before
it except the head segment which moves in the last direction input by the
player).
If a segment intercepts another segment it destroys it. The severed segment
then becomes bombs.
Number of mushrooms eaten (Used as a score)
Actors
Actors specifies an indescriminate number of items placed on the board that the
centipede interacts with when it comes into contact with them. The actors need
to be able to expand to include new actors with new effects.
Sprite
Board position
An affect
Right now we have two actors: mushrooms and bombs. Mushrooms are placed randomly
on the board at a fixed interval. Bombs are segments that have seperated from
the centipede. They each have an affect. Mushrooms cause a new segment to be
added to the centipede after X mushrooms have been consumed. Bombs cause the
game to immediately end.