Spacewar! 37 - A small retrospective.
Some topic
Here’s a recent list of things we might do, with the ones we’ve done removed and a couple added:
- Buttons
- Hard to see
- Hard to keep finger on
- Bad position
- Put in corners
- one more fragment
- fragments stick to sun?
- hyperspace
- sun / gravity (has gravity, does not look good)
- star field
- Ships bigger?
- Limit game size to stay off keys
- Hit count / damage amount
- settings console
- Information display (bullets left, score, …)
- Asteroids (frogger, etc)
- Game recycle
- Player ID
Let’s look at the buttons. They are all in a line at the edge of the screen. My fingers are not all in a line. I’m thinking we should have a setup that looks like this:
IMAGE FROM PAPER
We sketched this layout and it lets you rest your hands and fingers fairly comfortably. Now to figure out how to do it. We think it should be easy enough to test insideButton
instead of insideCircle
. Getting them created will be a bit interesting. Today, things look like this:
function Ship:init(shipNumber)
self.name = string.format("Ship %d", shipNumber)
self.missileLoad = Ship.MaxMissiles
self.timeLastFired = -Ship.TicksBetweenMissiles
self.shipNumber = shipNumber
self.hitpoints = Ship.MaxDamage
self.damageDealt = 5
self.pos = self:initialPosition()
self.heading = self:initialHeading()
self.vel = vec2(0,0)
self.controls = {
left = self:controlButton(HEIGHT/5),
right = self:controlButton(2*HEIGHT/5),
thrust = self:controlButton(3*HEIGHT/5),
fire = self:controlButton(4*HEIGHT/5)
}
U:addObject(self)
end
function Ship:controlButton(offset)
if self.shipNumber == 1 then
return Button(WIDTH-85, offset)
else
return Button(85, HEIGHT-offset)
end
end
-- HAVE YOU PUSHED TO GITHUB TODAY?
Button = class()
function Button:init(x, y)
self.name = string.format("Button %f %f", x, y)
self.pos = vec2(x,y)
self.radius = 50
self.pressed = false
self.capturedID = nil
self.cannotCollide = true
U:addTouched(self)
U:addObject(self)
end
function Button:draw()
pushStyle()
pushMatrix()
ellipseMode(RADIUS)
translate(self.pos:unpack())
fill(self:fillColor())
ellipse(0,0, self.radius)
popMatrix()
popStyle()
end
function Button:fillColor()
if (self.pressed) then
return color(0, 255, 0, 255)
else
return color(255,0,0,26)
end
end
function Button:touched(touch)
self.capturedID = self:getCapture(touch)
self.pressed = self:getPressed(touch)
end
function Button:getCapture(touch)
if touch.state == BEGAN and self:insideCircle(touch) and not self.capturedID then
return touch.id
elseif touch.state == ENDED and self.capturedID == touch.id then
return nil
else
return self.capturedID
end
end
function Button:getPressed(touch)
if not self.capturedID then
return false
elseif self.capturedID ~= touch.id then
return self.pressed
else
return self:insideCircle(touch)
end
end
function Button:insideCircle(touch)
return self.pos:dist(vec2(touch.x, touch.y)) <= self.radius
end
function Button:move()
-- don't
end
The calls to create the Button are providing a calculated center based on the ship number. The Button tests for being touched with insideCircle
, which will need to be changed to deal with the rectangles we have in mind.
Our careful calculations yesterday tell us that dimensions of 170x100 or 100x170 are about what we need. Let’s see what we can do.
Tozier asks whether we should create a new RectangularButton as opposed to modifying Button. Upon discussion we decide to try making the existing buttons rectangular, then moving them more or less last. We’ll modify Button rather than build a new class or subclass. After all, the old version will be in Git.
First, let’s draw squares in place:
function Button:draw()
pushStyle()
pushMatrix()
rectMode(RADIUS)
translate(self.pos:unpack())
fill(self:fillColor())
rect(0,0, self.radius)
popMatrix()
popStyle()
end
IMAGE HERE
That was easy, we just changed ellipseMode
to rectMode
and ellipse
to rect
. (Note the sketches of the new buttons in the lower part of the picture. Those are just drawn with a couple of lines in Main. Pay no attention to that.)
Let’s try making them other than square:
IMAGE HERE
function Button:draw()
pushStyle()
pushMatrix()
rectMode(RADIUS)
translate(self.pos:unpack())
fill(self:fillColor())
rect(0,0, self.radius, self.radius*1.7)
popMatrix()
popStyle()
end
Now let’s make touching work: right now we’re still just checking for inside the radius. I think to do this we’ll save some member variables for width and height … which we’ll later change to make the boxes vertical or horizontal as our “design” asks.
We begin by intention:
function Button:insideMe(touch)
local x = touch.x
local y = touch.y
local insideX = x >= myLeft and x <= myRight
local insideY = y >= myBottom and y <= myTop
return insideX and insideY
end
This isn’t complete of course but I wanted to share our thinking.
Tozier reports being easily confused by the lack of parentheses. I propose to wait and see how it turns out but it’s a valid point: we must cater to the easily confused insofar as possible.
function Button:insideMe(touch)
local x = touch.x
local y = touch.y
local myLeft = self.pos.x - self.width
local myRight = self.pos.x + self.width
local myBottom = self.pos.y - self.height
local myTop = self.pos.y + self.height
local insideX = x >= myLeft and x <= myRight
local insideY = y >= myBottom and y <= myTop
return insideX and insideY
end
-- with also:
function Button:init(x, y)
self.name = string.format("Button %f %f", x, y)
self.pos = vec2(x,y)
self.width = 50
self.height = self.width*1.7
self.pressed = false
self.capturedID = nil
self.cannotCollide = true
U:addTouched(self)
U:addObject(self)
end
This works sooner than I had anticipated. Let’s push the code … and done.
It’s worth mentioning that this went better than we thought, and differently from our plan. We had talked about using the rectangle mode where you mention x1, y1, x2, y2, or xLowerLeft, yLowerLeft, width, height, and here we are using xCenter, yCenter, and the half-width and half-height. We slid into this implementation when we decided to “do the simplest thing that could possibly work” and draw the squares where the circles used to be. Very interesting. Is this better or worse than plan?
There may be a tiny bit more calculation in insideMe
right now. If so, we can surely factor it out. Otherwise it feels quite nice.
We are left with putting the buttons where they belong, along the edges of the screen, and making the outer ones go in landscape and the inner ones portrait. (We confuse ourselves with those words, so I’ll leave them in in case we get in trouble later.)
Right now, the position of the buttons belongs to the Ship, so let’s let Ship deal with position and orientation of our new rectangles. Then we might want to revisit whether the objects are correctly partitioned.
-- Ship
self.controls = {
left = self:controlButton(HEIGHT/5),
right = self:controlButton(2*HEIGHT/5),
thrust = self:controlButton(3*HEIGHT/5),
fire = self:controlButton(4*HEIGHT/5)
}
U:addObject(self)
end
function Ship:controlButton(offset)
if self.shipNumber == 1 then
return Button(WIDTH-85, offset)
else
return Button(85, HEIGHT-offset)
end
end
This code is returning the position. Let’s put the half-width and half-height right in the constructor as well:
self.controls = {
left = self:controlButton(85, HEIGHT-50),
right = self:controlButton(50, HEIGHT- (100+85)),
thrust = self:controlButton(50, 100+85),
fire = self:controlButton(85, 50)
}
U:addObject(self)
end
function Ship:controlButton(x,y)
if self.shipNumber == 1 then
return Button(x,y)
else
return Button(WIDTH-x,HEIGHT-y)
end
end
We just ball-peened in these numbers, and we get buttons where they belong, although the two that have their long side parallel to the long side of the screen are not yet “turned”. We discuss whether to rotate them, or just to provide the other two values. I lean toward the latter because it’s simple and we figured out these coordinates manually: there’s no real math going on here.
self.controls = {
left = self:controlButton(85, HEIGHT-50, 85, 50),
right = self:controlButton(50, HEIGHT-185, 50, 85),
thrust = self:controlButton(50, 185, 50, 85),
fire = self:controlButton(85, 50, 85, 50)
}
U:addObject(self)
end
function Ship:controlButton(x,y, w2, h2)
if self.shipNumber == 1 then
return Button(x,y, w2, h2)
else
return Button(WIDTH-x,HEIGHT-y, w2, h2)
end
end
IMAGE HERE
I would like to see the edges of the buttons. Let’s see if we can make the line white.
function Button:draw()
pushStyle()
pushMatrix()
rectMode(RADIUS)
translate(self.pos:unpack())
strokeWidth(1)
stroke(255)
fill(self:fillColor())
rect(0,0, self.width, self.height)
popMatrix()
popStyle()
end
IMAGE HERE
Looks good, and the buttons work. The ships fly and shoot and generally cavort freely like space sheep. Time to push code … and done.
This has been a good day. Let’s stop before we screw this up. See you next time!