Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» FourLords Part I

teaching machines

FourLords Part I

October 29, 2012 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
buster, public.

At last week’s Buster meeting, we decided to implement a multi-pilayer version of Warlords, an Atari game that formed my young mind. I’m going to call it FourLords, because really the game doesn’t involve armies or anything on a war-scale. Plus, I want to stress the multiple Pis, multiple players aspect of the game. I drafted three initial steps to get a prototype up and running:

  1. Write a Rectangle class.
  2. Write a bouncing ball renderer.
  3. Write a collision-aware renderer.

1. Write a Rectangle class

Rectangles will be used for the bouncing ball, the forts, and the ultra-sensitive kings hidden away inside the forts. They also need to be movable and able to be checked for collisions with other rectangles.

class Rectangle:
  def __init__(self, l, r, b, t):
    self.l = l
    self.r = r
    self.b = b
    self.t = t

  def collidesWith(self, other):
    return not (self.r < other.l or self.l > other.r or self.t < other.b or self.b > other.t)

  def getVertices(self):
    return [self.l, self.b, 0.0,
            self.r, self.b, 0.0,
            self.l, self.t, 0.0,
            self.r, self.t, 0.0]

  def translate(self, dx, dy):
    self.l += dx
    self.r += dx
    self.b += dy
    self.t += dy

2. Write a bouncing ball renderer

This is the obligatory first step to any game.

The code behind it is a modification of our previous OpenGL renderers:

#!/usr/bin/env python

import time
import sys

from pyopengles import *
from geometry import *
from glop import *

WIDTH = 1680
HEIGHT = 1050
ASPECT = WIDTH / float(HEIGHT)

class FourLordsRenderer(Renderer):
  def __init__(self):
    Renderer.__init__(self)
    self.context = EGL(depth_size=8)
    self.startTime = time.time()

    self.vertexShader = """
      uniform mat4 projection;
      uniform mat4 modelview;

      attribute vec3 position;
      attribute vec3 normal;

      void main() {
        gl_Position = projection * modelview * vec4(position, 1.0);
      }
    """

    self.fragmentShader = """
      precision mediump float;

      uniform float time;
      uniform vec2 resolution;

      void main() {
        gl_FragColor = vec4(vec3(0), 1.0);
      }
    """

    self.binding = ((0, 'position'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    opengles.glUseProgram(self.program)
    self.modelviewUID = opengles.glGetUniformLocation(self.program, "modelview")
    self.projectionUID = opengles.glGetUniformLocation(self.program, "projection")
    self.resolutionUID = opengles.glGetUniformLocation(self.program, "resolution")
    self.timeUID = opengles.glGetUniformLocation(self.program, "time")
    opengles.glUseProgram(0)

    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUseProgram(0)

    opengles.glClearColor(eglfloat(0.85), eglfloat(0.35), eglfloat(0.15), eglfloat(1.0))

    # Let's make a new rectangle centered in the viewport with a random velocity.
    self.rectangle = Rectangle(-0.1, 0.1, -0.1, 0.1)
    self.velocity = Vector2.getRandom(1.0)

    self.vertices = eglfloats(self.rectangle.getVertices())
    self.checkError('after init')

    self.modelview = Matrix4.getIdentity()

    # Let's always show at least [-1, 1] x [-1, 1]. If our aspect ratio gives
    # us more real estate than that on either x or y, let's increase the span
    # shown on that axis.

    # If we have more height than width, show more on the y-axis.
    if ASPECT < 1.0:
      self.right = 1.0
      self.top = 1.0 / ASPECT

    # Otherwise, show more of the x-axis.
    else:
      self.right = 1.0 * ASPECT
      self.top = 1.0

    self.left = -self.right
    self.bottom = -self.top
    self.projection = Matrix4.getOrthoMatrix(self.left, self.right, self.bottom, self.top, -50.0, 50.0)

  def onKeyEvent(self, keycode):
    pass

  def onMouseEvent(self, event):
    pass

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT)

    opengles.glUseProgram(self.program)

    opengles.glUniform1f(self.timeUID, eglfloat(time.time() - self.startTime))
    opengles.glUniformMatrix4fv(self.modelviewUID, 1, GL_FALSE, eglfloats(self.modelview.data))
    opengles.glUniformMatrix4fv(self.projectionUID, 1, GL_FALSE, eglfloats(self.projection.data))

    opengles.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)

    opengles.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
    opengles.glUseProgram(0)

    self.checkError('after draw')

    openegl.eglSwapBuffers(self.context.display, self.context.surface)

    # Update rectangle's position based on its velocity and the time elapsed
    # since the last frame.
    self.rectangle.translate(self.deltaTime * self.velocity[0], self.deltaTime * self.velocity[1])
    self.vertices = eglfloats(self.rectangle.getVertices())

    # If we exceed the bounds of the visible space, let's reflect the velocity
    # vector about the normal of the boundary we crossed.
    if self.rectangle.l < self.left or self.rectangle.r > self.right:
      self.velocity[0] = -self.velocity[0]
    if self.rectangle.b < self.bottom or self.rectangle.t > self.top:
      self.velocity[1] = -self.velocity[1]

  def checkError(self, msg):
    e = opengles.glGetError()
    if e:
      print msg, 'Error: ', hex(e)
      sys.exit(1)

if __name__ == "__main__":
  with FourLordsRenderer() as renderer:
    render.go(WIDTH, HEIGHT, 0.02, renderer)

To store velocity, I also created a simple Vector2 class:

import math
import random

class Vector2:
  def __init__(self, x = 0.0, y = 0.0):
    self.coords = [x, y]

  def getLength(self):
    return math.sqrt(self.coords[0] * self.coords[0] + self.coords[1] * self.coords[1])

  def normalize(self):
    length = self.getLength()
    if length > 0.0:
      self.coords[0] /= length
      self.coords[1] /= length

  def __getitem__(self, i):
    return self.coords[i]

  def __setitem__(self, i, value):
    self.coords[i] = value

  def __str__(self):
    return repr(self.coords)

  @staticmethod
  def getRandom(magnitude = 1.0):
    angle = random.random() * 2.0 * math.pi
    return Vector2(math.cos(angle) * magnitude, math.sin(angle) * magnitude)

I’m finding that I do not appreciate Python’s mandatory requirements of including the invoking object in methods’ parameter lists and qualifying instance variable references with that object. Python provides a very thin veneer of object-orientation. The customary notation is only seen on method calls. Everywhere else methods look just like functions taking in structs. It doesn’t capitalize on the context benefits that a class provides. Most of my variable accesses are going to be to instance variables. Can’t we make the most common thing the easiest thing? Outlaw shadows of instance variables, require qualification for access to globals, and let’s eliminate qualified access to instance variables. I feel like I’m writing C code, not object-oriented code.

3. Write a collision-aware renderer

The next step is to write a renderer that bounces the ball around a field strewn with randomly-placed rectangles, which disappear when collided with. This one’s not done yet. I need to think about the best way to dynamically remove elements from the vertex array that won’t impose a linear time operation to reshuffle the array’s contents. Probably swapping out the last item in the list for the one collided with will work.

This is work for another day.