Week 9 – Reading Response

Physical Computing:
It was really interesting to see all the different forms that physical computing pieces can take. The examples given, despite only being a small glimpse into the genre, cover a whole bunch of mediums and correspondingly provide entirely new experiences. In order to do so, they each have to take into account the physical and digital components for both input and output, and figure out how to blend them all together into one working whole. What stood out to me the most was all the little tricks used to make sense of intuitive human motions such as hand movements, as well as the variations of ways to do so. That particular point was also echoed in the introduction, where it mentioned that the same idea can be produced in varying ways. Hopefully as the semester progresses, my own pieces will similarly become more complex and interactive.

Interactive Art:
After the first half of this class and the resulting midterm project, I can definitely identify with what the reading is getting at. The previous readings on different aspects of design come into play once again, albeit with more emphasis on the physical aspect this time around. The listening aspect builds on that and turns the creation process into a more iterative version of itself to presumably beneficial effect. I liked the comparison to telling an actor how to act versus giving them the tools to perform their craft, which naturally leads into the next analogy of the piece being a performance involving the audience. Going forward I’d like to keep this perspective in mind, and endeavour to provide a more comprehensive experience in my work.

Week 8 – Reading Response 1 (Attractive Things Work Better)

I found the initial anecdote of the three teapots to be especially interesting. Norman spends nearly a third of the reading comparing and contrasting them, noting that he might choose one over the others depending on the occasion or his mood. In stark contrast, my first thought was to wonder why anybody would one teapot let alone three. Beauty is really in the eye of the beholder here, since on the rare occasion I make tea it involves a mug and a pre-packaged teabag. Even his mention of displaying them on the windowsill just made me question why he would waste space doing so.

As for the larger discussion on aesthetic/usability and affect/cognition, I have some mixed feelings. The explanation of how the chemicals in your brain can completely warp your perception was very well-done, what with the examples of tightrope walking or opening an emergency door. That being said, the claim that “attractive things work better” feels far too heavy-handed (although Norman himself notes that the claim is heretical). The preceding two sentences mention that a product that looks good will encourage the user to overlook its flaws, and to me that hardly means ‘working better’. You could definitely argue that the emotional aspect could improve the user’s enjoyment, but that is countered by claiming that performance would be better with an uglier, more efficient version. You could also argue that attractiveness helps the product sell, but then you are outright avoiding the point of debate. I would agree more if his claim was about how attractive things face less criticism, and are accepted more easily.

Week 8 – Reading Response 2 (Her Code Got Humans on the Moon)

When I first saw the title of this reading, I assumed that it referred to the three mathematicians that starred in Hidden Figures (2016). I was instead pleasantly surprised by a fresh perspective that was similar yet different, and happens to result in the creation of software engineering. As a Computer Science major, it was very cool to see how software was viewed before it became commonplace, and get to learn about the factors that shaped its development into what it is today. It’s still hard to imagine how they managed to put a man on the moon while being stuck in the era where the term ‘physical computing’ was all too literal.

In that sense there is a clear tie-in to our Arduino work, what with having to wire components in by hand and having actual limitations on memory, although thankfully we still have access to an IDE. Even aside from the Interactive Media perspective, I got a lot out of this piece that can apply to the software engineering class I’m taking. For example, having to make asynchronous calls to the backend of our web application and dealing with potential errors. We’re actually currently in the middle of writing system tests for our next deliverable, so reading through the ‘That would never happen’ section was particularly painful.

Midterm Project – Space Shooter

Concept:
My midterm project was inspired by classic arcade games like Space Invaders and Galaga. I wanted to create my own shoot ‘em up game set in space, and try to capture that retro feel while throwing some new elements into the mix.
I made the choice to have the player character and their bullets be pixelated, while the enemies have a more modern look and shoot laser beams in order to create some distinct contrast. I also introduced additional obstacles in the form of slow-moving asteroids and diagonally-moving comets, both of which share in the more modern appearance. I only included sound effects relevant to the player character, so they lean towards the older side.

How it Works:
As for the actual gameplay, I decided to confine the player to a single static screen, where obstacles (including enemies) will continually descend from the top. Since I didn’t design multiple levels or an upward scrolling mechanic, I chose to increase the spawn rate of obstacles as the round goes on to increase the difficulty. The player will continue until their health runs out, at which point they are prompted to save their score.
The player earns points by shooting and destroying an obstacle, but they will also earn a smaller amount for simply allowing the obstacle to pass them and be cleared off-screen. If the player collides with the obstacle, they will instead lose points and destroy the obstacle.

Highlights:
Using OOP to manage different obstacle sub-types + Detecting and handling collisions during gameplay:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Obstacle {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 32;
this.health = 3;
}
display() {
this.update();
circle(this.x,this.y,this.radius*2);
}
update() {
if (gameRunning()) {
this.y += 0.5;
}
}
check() {
if (this.health <= 0) {return}
// // Flag obstacles below screen to be cleared
if (!onScreen(this)) {
// console.log("Destroy offscreen");
this.health = 0;
game.score += 5;
}
// // Check collision w/ player
if (checkCollision(this, game.player)) {
this.health = 0;
if (!game.player.shielded) {
game.score -= 10;
game.player.health -= 1;
playHurt();
if (game.player.health <= 0) {return}
game.player.iframes = 60;
game.player.shielded = true;
}
}
// // Check collision w/ player bullets
for (let i = 0; i < game.player.bullets.length; i++) {
if (checkCollision(this, game.player.bullets[i])) {
if (!game.player.bullets[i].spent) {
game.player.bullets[i].spent = true;
this.health -= 1;
if (this.health <= 0) {
game.score += 20;
}
}
}
}
}
}
class Enemy extends Obstacle{
constructor(x, y) {
// // Gameplay attributes
super(x, y);
this.radius = 32;
this.sprite = enemySprite;
this.firerate = 0.3;
this.offset = random(1000);
}
display() {
this.update();
image(this.sprite, this.x, this.y, 64, 60);
if (debug) {circle(this.x, this.y, this.radius*2)}
}
update() {
if (gameRunning() && onScreen(this)) {
this.x += sin((frameCount + this.offset) * 0.05) * 2;
this.y += 0.5;
if (triggerFire(this.firerate)) {
append(game.enemyBullets, new Bullet(
this.x, this.y+this.radius));
}
}
}
}
class Obstacle { constructor(x, y) { this.x = x; this.y = y; this.radius = 32; this.health = 3; } display() { this.update(); circle(this.x,this.y,this.radius*2); } update() { if (gameRunning()) { this.y += 0.5; } } check() { if (this.health <= 0) {return} // // Flag obstacles below screen to be cleared if (!onScreen(this)) { // console.log("Destroy offscreen"); this.health = 0; game.score += 5; } // // Check collision w/ player if (checkCollision(this, game.player)) { this.health = 0; if (!game.player.shielded) { game.score -= 10; game.player.health -= 1; playHurt(); if (game.player.health <= 0) {return} game.player.iframes = 60; game.player.shielded = true; } } // // Check collision w/ player bullets for (let i = 0; i < game.player.bullets.length; i++) { if (checkCollision(this, game.player.bullets[i])) { if (!game.player.bullets[i].spent) { game.player.bullets[i].spent = true; this.health -= 1; if (this.health <= 0) { game.score += 20; } } } } } } class Enemy extends Obstacle{ constructor(x, y) { // // Gameplay attributes super(x, y); this.radius = 32; this.sprite = enemySprite; this.firerate = 0.3; this.offset = random(1000); } display() { this.update(); image(this.sprite, this.x, this.y, 64, 60); if (debug) {circle(this.x, this.y, this.radius*2)} } update() { if (gameRunning() && onScreen(this)) { this.x += sin((frameCount + this.offset) * 0.05) * 2; this.y += 0.5; if (triggerFire(this.firerate)) { append(game.enemyBullets, new Bullet( this.x, this.y+this.radius)); } } } }
class Obstacle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.radius = 32;
    this.health = 3;
  }
  display() {
    this.update();
    circle(this.x,this.y,this.radius*2);
  }
  update() {
    if (gameRunning()) {
      this.y += 0.5;
    }
  }
  check() {
    if (this.health <= 0) {return}
    // // Flag obstacles below screen to be cleared
    if (!onScreen(this)) {
      // console.log("Destroy offscreen");
      this.health = 0;
      game.score += 5;
    }
    // // Check collision w/ player
    if (checkCollision(this, game.player)) {
      this.health = 0;
      if (!game.player.shielded) {
        game.score -= 10;
        game.player.health -= 1;
        playHurt();
        if (game.player.health <= 0) {return}
        game.player.iframes = 60;
        game.player.shielded = true;
      }
    }
    // // Check collision w/ player bullets
    for (let i = 0; i < game.player.bullets.length; i++) {
      if (checkCollision(this, game.player.bullets[i])) {
        if (!game.player.bullets[i].spent) {
          game.player.bullets[i].spent = true;
          this.health -= 1;
          if (this.health <= 0) {
            game.score += 20;
          }
        }
      }
    }
  }
}
class Enemy extends Obstacle{
  constructor(x, y) {
    // // Gameplay attributes
    super(x, y);
    this.radius = 32;
    this.sprite = enemySprite;
    this.firerate = 0.3;
    this.offset = random(1000);
  }
  display() {
    this.update();
    image(this.sprite, this.x, this.y, 64, 60);
    if (debug) {circle(this.x, this.y, this.radius*2)}
  }
  update() {
    if (gameRunning() && onScreen(this)) {
      this.x += sin((frameCount + this.offset) * 0.05) * 2;
      this.y += 0.5;
      if (triggerFire(this.firerate)) {
        append(game.enemyBullets, new Bullet(
          this.x, this.y+this.radius));
      }
    }
  }
}

Using helper functions instead of cramming everything in-line:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// // Determine whether to shoot on current frame
function triggerFire(rate) {
let trigger = Math.floor(60 / rate);
return frameCount % trigger === 0;
}
// // Check collision of two objects (circle bounding)
function checkCollision(one, two) {
if (dist(one.x, one.y, two.x, two.y) <=
(one.radius + two.radius)) {return true}
return false;
}
// // Spawn a new enemy/asteroid/comet
function spawnObstacle(type) {
let newX = Math.floor(random(30, width - 30));
let newY = Math.floor(random(-50, -200));
let obs;
switch(type) {
case 0:
obs = new Enemy(newX, newY);
append(game.enemies, obs);
break;
case 1:
obs = new Asteroid(newX, newY);
append(game.asteroids, obs);
break;
case 2:
newX = newX % 30;
obs = new Comet(newX, newY);
append(game.comets, obs);
break;
default:
console.log("Spawn " + type);
}
append(game.obstacles[type], obs);
if (debug) {console.log("Spawn new " + type)}
}
// // Standardize button styling for menu
function doStyle(elem) {
// // Styling
elem.style("font-size", "16px");
elem.style("font-family", "sans-serif");
elem.style("background-color", "rgb(20,20,20)");
elem.style("color", "whitesmoke");
elem.style("cursor", "pointer");
// // Hover behaviour
elem.mouseOver(() => {
elem.style("background-color", "firebrick");
});
elem.mouseOut(() => {
elem.style("background-color", "rgb(20, 20, 20)");
});
}
// // Handle gameover
function triggerGameover() {
gameState = 'gameover';
game_bgm.stop();
// player_shoot.stop();
// player_hurt.stop();
if (game) {
game.gameover = true;
// // Play win sound for score (temp)
if (game.score > 500) {
win_sound.play();
return;
}
}
lose_sound.play();
}
// // Determine whether to shoot on current frame function triggerFire(rate) { let trigger = Math.floor(60 / rate); return frameCount % trigger === 0; } // // Check collision of two objects (circle bounding) function checkCollision(one, two) { if (dist(one.x, one.y, two.x, two.y) <= (one.radius + two.radius)) {return true} return false; } // // Spawn a new enemy/asteroid/comet function spawnObstacle(type) { let newX = Math.floor(random(30, width - 30)); let newY = Math.floor(random(-50, -200)); let obs; switch(type) { case 0: obs = new Enemy(newX, newY); append(game.enemies, obs); break; case 1: obs = new Asteroid(newX, newY); append(game.asteroids, obs); break; case 2: newX = newX % 30; obs = new Comet(newX, newY); append(game.comets, obs); break; default: console.log("Spawn " + type); } append(game.obstacles[type], obs); if (debug) {console.log("Spawn new " + type)} } // // Standardize button styling for menu function doStyle(elem) { // // Styling elem.style("font-size", "16px"); elem.style("font-family", "sans-serif"); elem.style("background-color", "rgb(20,20,20)"); elem.style("color", "whitesmoke"); elem.style("cursor", "pointer"); // // Hover behaviour elem.mouseOver(() => { elem.style("background-color", "firebrick"); }); elem.mouseOut(() => { elem.style("background-color", "rgb(20, 20, 20)"); }); } // // Handle gameover function triggerGameover() { gameState = 'gameover'; game_bgm.stop(); // player_shoot.stop(); // player_hurt.stop(); if (game) { game.gameover = true; // // Play win sound for score (temp) if (game.score > 500) { win_sound.play(); return; } } lose_sound.play(); }
// // Determine whether to shoot on current frame
function triggerFire(rate) {
  let trigger = Math.floor(60 / rate);
  return frameCount %  trigger === 0;
}
// // Check collision of two objects (circle bounding)
function checkCollision(one, two) {
  if (dist(one.x, one.y, two.x, two.y) <= 
      (one.radius + two.radius)) {return true}
  return false;
}
// // Spawn a new enemy/asteroid/comet
function spawnObstacle(type) {
  let newX = Math.floor(random(30, width - 30));
  let newY = Math.floor(random(-50, -200));
  let obs;
  switch(type) {
    case 0:
      obs = new Enemy(newX, newY);
      append(game.enemies, obs);
      break;
    case 1:
      obs = new Asteroid(newX, newY);
      append(game.asteroids, obs);
      break;
    case 2:
      newX = newX % 30;
      obs = new Comet(newX, newY);
      append(game.comets, obs);
      break;
    default:
      console.log("Spawn " + type);
  }
  append(game.obstacles[type], obs);
  if (debug) {console.log("Spawn new " + type)}
}
// // Standardize button styling for menu
function doStyle(elem) {
  // // Styling
  elem.style("font-size", "16px");
  elem.style("font-family", "sans-serif");
  elem.style("background-color", "rgb(20,20,20)");
  elem.style("color", "whitesmoke");
  elem.style("cursor", "pointer");
  // // Hover behaviour
  elem.mouseOver(() => {
    elem.style("background-color", "firebrick");
  });
  elem.mouseOut(() => {
    elem.style("background-color", "rgb(20, 20, 20)");
  });
}
// // Handle gameover
function triggerGameover() {
  gameState = 'gameover';
  game_bgm.stop();
  // player_shoot.stop();
  // player_hurt.stop();
  if (game) {
    game.gameover = true;
    // // Play win sound for score (temp)
    if (game.score > 500) {
      win_sound.play();
      return;
    }
  }
  lose_sound.play();
}

Areas for Improvement:
Since I spent most of my time on the gameplay itself, I feel like I lost out on the visual appeal in several places. For example, my menu screen has some styling on the buttons, but the font is fairly standard instead of using a pixelated font to add to the retro aesthetic. I also had started to work on a leaderboard system, but I belatedly realized that it would download a CSV file to the computer instead of saving the results to the relevant asset file.
Another area for improvement is the gameplay itself. I began to add power ups that would spawn in the playable area, but didn’t have time to fully implement them. The sound design was also a last minute addition that I completely forgot about until I started writing the blog post. Finally, the difficulty ramp-up could use some adjustment. Each obstacle type has semi-randomized parameters within differing bounds, but it still feels like something is a bit off about it.


(Fullscreen)

Week 5 – Midterm Progress

Concept:
My midterm project was inspired by classic shoot ‘em up games like Space Invaders, Galaxian/Galaga, or the countless variations that followed. These games had a huge impact on video games and pop culture as a whole, and have an iconic style that brings back childhood memories for many. I wanted to try and emulate this retro aesthetic and provide an enjoyable take on the space shooter genre.
I am still undecided on the specific end goal of my game, since I can see the appeal of both an endless rush to see how long you can survive as opposed to clearing stages and progressing. I am leaning towards the former and having enemies speed up as time progresses, as an homage to how in Space Invaders the game would speed up since less enemies being on screen meant the machine was able to run faster. Either way, I intend to provide a simple experience of piloting a spaceship and taking on enemy ships while dodging obstacles like asteroids and comets.

Design:
As previously mentioned, I wanted to mainly focus on pixelated sprites to fit the time period. The backgrounds and menus, on the other hand, will probably be more modern by comparison for the sake of providing a better overall experience. The gameplay itself will get harder as the player progresses, through things like having more enemies that move/shoot faster, and maybe gradually adding in obstacles as you reach certain checkpoints.
So far, I have created classes for the player character and enemy ships, the bullets being fired, and a game class to keep track of the ongoing round. The ship and bullet classes contain attributes like their position and movement, current sprite, health and fire rate, etc. The game class keeps track of the time and score, has arrays for enemies and obstacles, and keeps track of the game state (e.g. ongoing, win, loss).

Challenges:

  • Keeping track of scenes being displayed for menus, game levels, win/loss screens, etc.
  • Detecting collisions during gameplay, determining whether the collision needs to be addressed, and handling it accordingly (e.g. destroy enemy ship on contact with the player’s bullet)
  • Figuring out how to display menus in an intuitive manner, and handling clicking on buttons or alternatively navigating via keyboard inputs.

Risk Prevention:
I have started working on some helper functions for generic tasks like reading/handling keyboard inputs at different points in time, as well as checking for collisions between objects through rough circular hitboxes (in order to use radii as a measure). What I am still working on is coming up with a way to handle mouse interactivity on menus and putting it in one helper function.

Week 5 – Reading Response

Given how important sight is to humans in regards to navigating and interacting with the world around us, granting similar abilities to a machine is a fascinating concept. Of course, it introduces just as many technical issues as it does opportunities, and what little I do know about computer vision/graphics is that it gets complicated very quickly. That aspect also shows just how impressive the human body is, since it takes huge amounts of work to create even a basic emulation of what comes naturally to us. The examples mentioned in the reading (frame differencing, background subtraction, and brightness thresholding) seem somewhat straightforward, but they each rely on very specific criteria to achieve their purposes.

There were a number of points made throughout the reading that stood out to me. For one, the beginning of the text mentions that most early applications of computer vision were military in nature, due to the prohibitive nature of the field at the time. While the technology is now more available than ever, the potential for misuse is also just as high. This has been seen in more general cases in the past few years like AirTag stalking, Zoombombing, etc. Computer vision is a particularly bad case given how cameras are literally everywhere nowadays, ready to collect PII or otherwise violate our privacy. A less concerning point I liked was how in order to optimize the digital performance of computer vision, you have to provide the right physical environment for it to work in. While it is rather obvious when you think about the requirements and constraints each technique has, I appreciated the duality of the process and allusion to how “two in harmony surpasses one in perfection.”

Week 4 – Generative Text

Concept:
This piece is meant to imitate a page turning, but instead of the expected behaviour it slides a new page down from the top. The text itself is pulled from a .txt file, which contains bits of placeholder text (Lorem ipsum dolor) as well as snippets of text from the p5.js reference that pertain to implementing text.

Highlight:
My highlight is still fairly simple, but I chose it since I often forget to include bounds checking or error handling in my code and end up causing myself problems down the road. This snippet checks to make sure that the newly created page is not receiving empty input for its inner text, and cleans up the pages array once a certain number of pages have been created while leaving only the most recent instance.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// // Create a new page on click
let newText = txt[floor(random(txt.length - 1))];
let tries = 0;
// // Make sure new page's text is not empty
while (!newText && tries < 10) {
newText = txt[floor(random(txt.length - 1))];
tries++;
}
// // Cull older pages at some upper limit
if (pages.length >= 10) {
pages = subset(pages, pages.length - 1, 1);
console.log("reset");
console.log(pages);
}
// // Create a new page on click let newText = txt[floor(random(txt.length - 1))]; let tries = 0; // // Make sure new page's text is not empty while (!newText && tries < 10) { newText = txt[floor(random(txt.length - 1))]; tries++; } // // Cull older pages at some upper limit if (pages.length >= 10) { pages = subset(pages, pages.length - 1, 1); console.log("reset"); console.log(pages); }
// // Create a new page on click
  let newText = txt[floor(random(txt.length - 1))];
  let tries = 0;
  // // Make sure new page's text is not empty
  while (!newText && tries < 10) {
    newText = txt[floor(random(txt.length - 1))];
    tries++;
  }
  // // Cull older pages at some upper limit
  if (pages.length >= 10) {
    pages = subset(pages, pages.length - 1, 1);
    console.log("reset");
    console.log(pages);
  }

Embed:

Reflection:
I’m not super satisfied with this piece, doubly so since I tried a few different concepts that I was unable to get to work properly. I had been having issues with getting the data to load from a file, and only managed to get it to work once I pivoted to this idea. I had also wanted to have the sliding direction change between the top, sides, and bottom, either changing randomly or going in a clockwise direction. This week was a bit rough in terms of workload so I was unable to spend as much time adding features to this piece as I would have liked.

Week 4 – Reading Response

This reading was both very illuminating and validating, especially in regards to the initial discussion of confusing doors. I had always found UI design to be an interesting topic, and this glimpse into the field of design as a whole was a lot more involved than I was prepared for. I was definitely impressed by how much work goes into properly designing a product, only for the bestowed ease-of-use resulting in users remaining completely oblivious to the effort put in. In that sense, it is similar to how good audio mixing in a song or film will go unnoticed, but bad audio will completely pull you out of the experience. There were also some good points made later on about having to carefully balance the different aspects of design, such as signals and feedback being informative yet unobtrusive, or products being feature-rich while remaining affordable.

As for applying these design principles to interactive media, I am at a bit of a loss. In the past few weeks I have experimented with using simple forms of interactivity like clicking or using arrow keys to move an object in the p5.js scene. These ended up being fairly intuitive in the sense that they are reasonable choices that one would expect when using a computer. However, if the user was not aware that these pieces were interactive to begin with, a huge chunk of the experience is lost. I had left comments at the top of the code detailing the viable inputs, but of course this requires navigating from the WordPress site to the p5.js editor. A straightforward way to deal with this would be to include text instructions in the piece itself, or having some sort of help menu, but I still feel like this would negatively impact the more artistic pieces.

Week 3 – Reading Response

I liked how the author took the vague concept of ‘interactivity’ and was able to neatly break it down into three simple parts. When I think of the word, I imagine a fairly basic system where an otherwise one-sided info dump is intentionally broken up by prompting the user to engage with it. For example, an educational application might have the user answer questions during or between readings/videos. The idea of a three-step process, where each stage requires effort and intent, makes things much more concrete to think about. The manner in which the author approaches each point was also very interesting. For example, they kept bringing up cases that were either too trivial or too exaggerated to fit into the initial argument, as well as mentioning that certain people would argue on behalf of those edge cases. This naturally led into the discussion of measuring interactivity through ‘degrees’ instead of as a yes/no, which made the argument even easier to digest. The abundance of anecdotes, similes, and metaphors also did a lot to illustrate their points while providing a source of entertainment that helped me get through the reading.

In my own work for this class, I have made some effort for all of my weekly production pieces to include some form of basic interactivity. The second week’s piece allowed the user to move their specific block around and paint in the gridlike canvas, along with some other functionality, and this week’s piece provides some ability to manipulate the layered shapes and essentially create new variations on the spot, or to focus on the appearance while the layers are in motion. I wouldn’t call these strong forms of interactivity, since it really caps out at moving what’s already in the scene around. A relevant quote that stood out to me was that “participation is not the same thing as interaction,” and in these cases the user is really just participating due to the very limited input and feedback.

Week 3 – OOP

Concept:
This generative artwork was inspired by kaleidoscopes, with specific regard to the shifting patterns they display as you rotate it. I used the OOP principles we covered to make a Hexagon class and relevant properties/methods for it, and combined it with a 2D array to create multiple layers of hexagonal grids. I implemented randomness by randomly setting the colour and opacity of each hexagon, with the latter being constrained between 30 and 80 to allow for some clear overlap and blending between layers. I initially planned to have three layers, being controlled by mouse/arrows/wasd respectively, but I ended up scrapping the mouse movement since I liked the imagery of peeling back the upper two layers to reveal the remaining one, plus using keys allowed me to set a specific speed value instead of being at the whim of the cursor’s movement.

Highlight:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// // Fill a specified layer with hexagons
function fillLayer(index) {
// // Loops to populate layer
let offsetCol = false;
for (let x = -(width/2); x < width*1.5; x += 1.5*hexSize) {
for (let y = -(height/2); y < height*1.5; y += 2*hexSize) {
// // Offset every other column --> interlocking pattern
if (offsetCol && y === -(height/2)) {
y += hexSize;
}
layers[index].push(new Hexagon(x, y, hexSize));
}
offsetCol = !offsetCol;
}
}
// // Fill a specified layer with hexagons function fillLayer(index) { // // Loops to populate layer let offsetCol = false; for (let x = -(width/2); x < width*1.5; x += 1.5*hexSize) { for (let y = -(height/2); y < height*1.5; y += 2*hexSize) { // // Offset every other column --> interlocking pattern if (offsetCol && y === -(height/2)) { y += hexSize; } layers[index].push(new Hexagon(x, y, hexSize)); } offsetCol = !offsetCol; } }
// // Fill a specified layer with hexagons
function fillLayer(index) {
  // // Loops to populate layer
  let offsetCol = false;
  for (let x = -(width/2); x < width*1.5; x += 1.5*hexSize) {
    for (let y = -(height/2); y < height*1.5; y += 2*hexSize) {
      // // Offset every other column --> interlocking pattern
      if (offsetCol && y === -(height/2)) {
        y += hexSize;
      }
      layers[index].push(new Hexagon(x, y, hexSize));
    }
    offsetCol = !offsetCol;
  }
}

Embed:

Reflection:
I’m pretty happy with how this piece turned out, in both the creative and programmatic aspects. I felt that I had a clear vision going in this time, and was able to actually realize it. My code itself was better contained after compartmentalizing and encapsulating into functions and methods, and I was successful in cutting down on needlessly making functions. On the creative side, I ran into a number of happy little accidents that ended up improving my end result, which was a breath of fresh air.

Despite this specific improvement, I still definitely need to plan things out more, since I found myself having to constantly readjust my code in order to deal with things like messing up the math for my hexagons or implementing the movement functionality with keyIsPressed() instead of keyIsDown().