21. Exercises,对象管理,组合
3.17,补完 exercises。
Exercise 44
创建三个 Room:
CircleRoom,在屏幕中央画一个圆;RectangleRoom,在屏幕中央画一个矩形;以及PolygonRoom,在屏幕中央画一个多边形。再把F1、F2、F3绑定成切换到这三个 Room 的按键。
lua
function love.load()
local room_files = {}
recursive_enumerate("rooms", room_files)
require_files(room_files)
input = input_handler()
timer = enhanced_timer()
current_room = nil
input:bind("f1", function() goto_room("circle_room") end)
input:bind("f2", function() goto_room("rectangle_room") end)
input:bind("f3", function() goto_room("polygon_room") end)
end
function love.update(dt)
if current_room then current_room:update(dt) end
end
function love.draw()
if current_room then current_room:draw() end
end
function goto_room(room_type, ...)
current_room = _G[room_type](...)
endlua
circle_room = object:extend()
function circle_room:new()
end
function circle_room:update(dt)
end
function circle_room:draw()
love.graphics.circle("fill", 400, 300, 50)
endlua
rectangle_room = object:extend()
function rectangle_room:new()
end
function rectangle_room:update(dt)
end
function rectangle_room:draw()
love.graphics.rectangle("fill", 400 - 100/2, 300 - 50/2, 100, 50)
endlua
polygon_room = object:extend()
function polygon_room:new()
end
function polygon_room:update(dt)
end
function polygon_room:draw()
love.graphics.polygon(
"fill",
400, 300 - 50,
400 + 50, 300,
400, 300 + 50,
400 - 50, 300
)
endExercise 48
创建一个带有
Area的StageRoom。然后再创建一个继承自GameObject的Circle对象,并让Stage每隔 2 秒在随机位置生成一个这样的实例。每个Circle实例都应该在 2 到 4 秒之间的随机时间后自我销毁。
game_object + area + room 三层解耦:
- game_object 只管自己 + 一个局部 timer
- area 管一堆对象,统一 update / draw 和清理 dead
- room(stage_with_area)组合 area + 自己的 timer 做关卡逻辑
lua
game_object = object:extend()
function game_object:new(area, x, y, opts)
local opts = opts or {}
if opts then
for k, v in pairs(opts) do
self[k] = v
end
end
self.area = area
self.x, self.y = x, y
self.id = uuid()
self.timer = enhanced_timer()
self.dead = false
end
function game_object:update(dt)
if self.timer then self.timer:update(dt) end
endlua
area = object:extend()
function area:new(room)
self.room = room
self.game_objects = {}
end
function area:update(dt)
for i = #self.game_objects, 1, -1 do
local obj = self.game_objects[i]
obj:update(dt)
if obj.dead then table.remove(self.game_objects, i) end
end
end
function area:draw()
for _, obj in ipairs(self.game_objects) do
obj:draw()
end
end
function area:add_game_object(game_object_type, x, y, opts)
local game_object = _G[game_object_type](self, x, y, opts)
table.insert(self.game_objects, game_object)
return game_object
endlua
stage_with_area = object:extend()
function stage_with_area:new()
self.area = area(self)
self.timer = enhanced_timer()
self.timer:every(2, function()
self.area:add_game_object('circle', random(0, 800), random(0, 600))
end)
end
function stage_with_area:update(dt)
self.area:update(dt)
self.timer:update(dt)
end
function stage_with_area:draw()
self.area:draw()
endlua
circle = game_object:extend()
function circle:new(area, x, y, opts)
circle.super.new(self, area, x, y, opts)
self.timer:after(random(2, 5), function() self.dead = true end)
end
function circle:update(dt)
circle.super.update(self, dt)
end
function circle:draw()
love.graphics.circle("fill", self.x, self.y, 50)
endExercise 49
创建一个不带
Area的StageRoom。然后创建一个不继承GameObject的Circle对象,并让Stage每隔 2 秒在随机位置生成一个实例。这个Circle实例也应该在 2 到 4 秒之间的随机时间后自行消失。
不用 Area 自己手写管理所有游戏对象。
lua
stage_without_area = object:extend()
function stage_without_area:new()
self.game_objects = {}
self.timer = enhanced_timer()
self.timer:every(2, function()
table.insert(self.game_objects, circle(self, random(0, 800), random(0, 600)))
end)
end
function stage_without_area:update(dt)
for i = #self.game_objects, 1, -1 do
local obj = self.game_objects[i]
obj:update(dt)
if obj.dead then table.remove(self.game_objects, i) end
end
self.timer:update(dt)
end
function stage_without_area:draw()
for _, obj in ipairs(self.game_objects) do
obj:draw()
end
endlua
circle = object:extend()
function circle:new(x, y)
self.x, self.y = x, y
self.dead = false
self.timer = enhanced_timer()
self.timer:after(random(2, 4), function() self.dead = true end)
end
function circle:update(dt)
self.timer:update(dt)
end
function circle:draw()
love.graphics.circle("fill", self.x, self.y, 50)
endExercise 50
第 1 题的解法里引入过一个
random函数。请扩展这个函数,让它在只传一个参数时,也能正常工作,并返回一个介于 0 和该参数之间的随机实数。另外,再扩展一下,让min和max可以反着传,也就是第一个值允许比第二个值更大。
单参时视为 0..max,并允许 min > max 的情况先交换。这样调用更自由,但会让“传参错误”更难被发现,调试时要有心理准备。
lua
function random(min, max)
if not max then
max = min
min = 0
end
if min > max then
min, max = max, min
end
return love.math.random() * (max - min) + min
end