Uh mazing: 01 An introduction to 3d game development

For this post, we’ll cover a what goes into building a 3d game. Fun, right? Maybe not super exciting but there are some important concepts we need to cover. Without further ado, getting started designing a video game for newbs.

If you’re interested in building video games, you’ve probably played a video game or two in your time. I know I’ve wasted way to many hours… When you’re playing any 3d game, you’re perspective of the world is usually either 1st person (most shooters) or 3rd person (most RPG’s) Or, you might observing from above (most an RTS). It turns out, these don’t make much difference once you get further into building the world. What you probably don’t think about, is the fact that you, or your avatar, are not moving. That’s right – in a 3d game: You don’t move – the rest of the world does.

In a 3d game: You don’t move, the rest of the world does

Graphics Rendering, a summarized explanation

Now, let me explain how graphics work, in a nutshell.

  1. You have a flat 2d screen.
  2. The screen has pixels!
  3. Just like a painter, you are tricking the human eye into seeing 3d
  4. Graphics rendering starts with a single pixel, moves to a line, then a triangle.
  5. The entire world is made of triangles
  6. Every detail added on top of that, is just enhancing groups of triangles.

Pretty simple right? Drawing triangles? I tend to think so. The real magic with the triangles – comes with positioning, and movement rules. Positioning is when things start to get a bit more complicated.

I took a video game design course in college. Some of the best take-aways from that course were which libraries to use, and, how to use the positioning. The easiest way I can explain positioning, or coordinates, is to start with a square. Now, draw two lines, one from top to bottom, the other from left to right. You now have two axis. Usually, the top to bottom one is the Y axis, and the left to right one is the X axis. So – if you move the Y axis along the X axis (draw the top to bottom bar a little to the right from where it started) You’ll have moved to a new coordinate along the X axis. The way we represent these positions in software are with Vectors. vector(1.0f, 2.0f) would mean a position of 1 on the x and 2 on the y axis.

Rendering

Now, lets go 3d! Remember the square, and drawing the lines across it? Do that with a cube. Yes, you’ve suddenly got a third line going from front to back. This is the z-axis. A vector can represent this as well. vector(1.0f, 2.0f, 1.0f) where the 3rd item is the position on the z-axis.

Now, it’s great to travel on axis – but if we can only look one direction, how do we turn, or look up? This is where rotation comes into play. And here’s where the programming gets tricky – because now, instead of just moving the world around your perspective – you need to rotate the world around your perspective, yet not rotate everything in the world at the same time! Fortunately, this is where other’s work can be helpful. Rendering engines take care of a lot of the complicated math using well known, powerful algorithms. If you’re still curious about using a camera approach, here is an example of a camera, and render method using C# and OpenGL

Textures, Sounds, and Networking

It isn’t enough to simply draw lines, squares, or donuts. For a game to be immersive it will need the donut to look like it got home from the bakery, the line will need to go ‘zap’ as it flashes by, and the kid across town is going to need to know where the square is. These auxiliary pieces to a game are crucial to some of the best games out there. To some extent, these can be overlooked in 3d world development. At the same time, the can’t be missed.

These will be covered a bit later on – there are tutorials out there already. And it is possible to develop the piece in some isolation. Well, textures might belong to the engine. that said, there are lots of opportunities to cover them.

Summary

This introduction covers some of the different high level aspects of the 3d world. Because 3d development is such a large item. Having a plan on what to attack next is useful to avoid scope creep and other difficulties getting anything working. As this guide is built out – it will somewhat follow my own approach to a development plan.

Guide: Build a RESTful API microservice with PHP

Too many times I’ve seen people lost in the weeds arguing about endpoints, design patterns, reusability, etc. It turns out – with a decent microservice – the architecture lends itself to simplicity. For this guide – I’ll take you step by step on how to standup a minimal Restful API Microservice using PHP. I’ll be using the Waryway Microserver Engine.

Setup the Project

This guide won’t cover adding your project to GIT.

  • Create your project directory
  • Add a composer.json file
  • Add the dependencies
    • waryway/micro-service-engine
    • waryway/php-traits-library
  • Add the autoloader to the composer.json
  • Add the src directory
"autoload": {
  "psr-4": {
    "OrgName\\ImagerServerRest\\": "src/"
  }
}

At this point, your project is going to look something like this:

ImageServerRest
  src
    ...
  vendor
    ...
  composer.json
  composer.lock

Create an initial endpoint

For the initial endpoing, I’m using the example from the readme.md.

  1. Copy the Router.php from example
  2. Replace Waryway\example with OrgName\ImageServerRest
  3. Change the setRoute to only ‘/’ and respond with ‘Hello World
    • $this->setRoute(['GET', 'POST', 'PUT', 'DELETE'], '/', [__CLASS__,'helloWorld']);
    • This was mostly just to provide a ‘test’ endpoint.
  4. Run composer update
  5. Run vendor\bin\server OrgName\ImageServerRest 0.0.0.0:99
  6. In a browser, navigate to localhost:99
Tada! You got a ‘Hello World’ response.

Add a real endpoint

The real cool thing. Adding an endpoint. If you ever want to add an endpoint, make certain to use a proper router. Frameworks have them. I like nikic’s fastrouter in PHP, or Mux is also fine in Golang.

Not getting too fancy – I just add a new route (/images), and a route handler (getImageStatistics) – and set them up to return according to what I find out. The example itself gets rather long – so I’ll simply link to it in github. Once I’ve gotten all the changes in place, I can run vendor\bin\server.bat orgname\ImageServerRest 0.0.0.0:99 and try out the URL. Eureka! http://127.0.0.1:99/images now returns a list of information about the available images.

Make it Conventional

It’s one thing to build an endpoint. It’s another to publish an endpoint. What I’m saying is this: If all lightbulbs had different size sockets, it would be much harder to go to the store to buy lightbulbs. In the same way – if all RESTful api’s have a similar standard for definition to clients, they become much simpler to use overall. The OpenAPI Specification provides just that, and Zircote/Swagger-PHP is a great tool to implement it.

Start by updating the composer.json with the swagger-php composer setting as a dev require. Then run composer update. Who knows, maybe Waryway will add it to the microservice engine at some point.

"require-dev": {
  "zircote/swagger-php": "dev-master"
},

Using the documentation from Zircote is pretty straightforward and the annotations are pretty obvious. That said – I ended up using this command to generate a nice little swagger.json file:

vendor\bin\openapi.bat --pattern "*.php" --output "./src/swagger.json" --format json ./src

The easiest way to see if the generated file works correctly, is to use the swagger editor, and paste the file contents into it. The editor will automatically convert any json to yaml – and will nicely update the generate API documentation. It’s really neat!

example of editor.swagger.io

Another cool trick with swagger, is the simplicity of adding the swagger view into the codebase itself, and exposing the entire utility via an endpoint. For this guide, I’m simply adding the swagger doc to view it at /swagger.json

Wrap the Package

If you thought containers weren’t going to make an appearance while talking about microservices, you should have paid for insurance. If you are considering the cardboard boxes, plastic totes, or even shipping containers – you might want to brush up on Docker!

The first piece of wrapping a package, is to take a step back from the /src directory and create a /build directory. The goal here, is that the build directory never actual lives where the program is deployed. Once you’ve added the build directory – let’s add a Dockerfile. Now, it doesn’t have to be a fancy Dockerfile – at least not for getting started. I am going to use the 7.3.10-cli-alpine from Docker Hub as a base image.

Note: The only files you really want to work with are your composer.json, composer.lock, and src directory.

There are many different flavors of docker files. For this one, I’m not really optimizing for size. I don’t clear the composer cache, for example. Notice however, that the swagger.json generator is included in the build. I also added the build command I used as a comment.

FROM php:7.3.10-cli-alpine

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
COPY ./composer.json ./composer.json
COPY ./composer.lock ./composer.lock
RUN composer install
COPY ./src ./src
RUN composer dump-autoload
RUN mkdir -p /assets/images
RUN ./vendor/bin/openapi --pattern "*.php" --output "./src/swagger.json" --format json ./src
EXPOSE 80

CMD php /vendor/bin/server orgname\\ImageServerRest 0.0.0.0:80

# docker build -t orgname/image-server:v1 -f build\Dockerfile .

Once you’ve gotten the Dockerfile built – simply kick it off, and remember to mount the ‘images’, or else there will be none.

docker run -it -p 80:80 -v C:/development/ImageServerRest/assets/images:/assets/images --name test orgname/image-server:v1

What now?

Well, now we’ve built a microservice, put it in a container, defined it with swagger, and even got it up and running locally – the next logical step is to ship the container! You’ll need to harden it (because containers are best not run as root). You’ll need to get it to a registry (dockerhub is my favorite currently). CICD for the entire project would be a huge boon… And, of course! you’ll probably want to write a different endpoint then my contrived ‘image stats’ endpoint.

I hope you’ve enjoyed this guide. If you have any problems following it – feel free to comment and ask about it! If there is a bug in the code – a new issue on GitHub would be greatly appreciated. If you’d like more guides like this: like, subscribe, and comment! Finally, thanks for reading!

Guide: Implementing a PhpUnit Development Environment in PhpStorm

Preperation

For this example, I will be starting with the following:

  • PhpTraits repository https://github.com/Waryway/PhpTraits
  • PhpStorm Project
    • Make certain you have a php interpreter referenced in your storm project
    • Also make certain you have Composer referenced by PHPStorm
    • A desire to test.allTheThings()
Adding the PHP interpreter reference.

Installing Libraries

First, use composer do some work. We need a copy of phpunit. Get it with this:

"require-dev":{
    "phpunit/phpunit":"6.4.3"
}

As this is a ‘library’ style app – use an exact version. The lock file gets ignored in favor of precise dependencies in the composer.json.

Now run composer update to pull in the requested phpunit package.

Setup phpunit in the general settings, under phpunit

Adding a new test

Or rather, a test skeleton

  1. Open the file to test in the Project View
  2. Place you cursor on a method to test within the file
  3. Go To Navigate -> Test
  4. Create New test
  5. I tend to set mine up with a test prefix to keep tests more obviously separate from code.
  6. Click Ok then Open up the new file
  7. Add a line to pull in the composer autoloader
  8. Clean up the Namespace reference to the TestCase if you are using PSR-4
  9. Disclaimer: Strongly avoid namespacing unit tests. They should not be built out like a code base. They are testing units.
  10. Add an empty testHydrate method.
  11. Add a super obvious assertion.
  12. Add a phpunit.xml fileI put it in the test directory.
  13. Run the test (At this point, I needed to restart phpstorm to detect that I actually had a ‘test’ to run.
  14. Build the tests out further.

Conclusion

Ending of the beginning

I’ve gone ahead and built out the ‘hydrate’ test against a trait. Note the use of the trait object that phpunit provides. Pretty slick, right?  I’ll continue to build out this repository – core libraries need the most directed test coverage.

Even adding the ‘test’ cases around the hydrate method – I have found use cases I didn’t consider while writing the code – and have adjusted the code to reflect the behavior I expect.