2011年5月11日 星期三

在 LÖVE 實作追蹤系統

前言

之前用 actionscript3.0 做守塔遊戲的時候,有做了一個簡單的追蹤系統,現在把它移植到 LÖVE 上。

追蹤系統可以用在導彈,或是其它需要尾隨的應用。

 

範例程式下載

Tracker.love

 

情境說明

建立四個半徑不同大小的圓,追蹤的速度與半徑大小成反比;最小的圓追滑鼠,剩下的分別追比自己小一階的圓。

 

Screenshot

SNAGHTMLf1fcca

SNAGHTMLf2b4b0

 

程式碼(都在範例檔案內)

main.lua

 
require("lua/Circle")
require("lua/Tracker")
 
function love.load()
    mouse = {}
    
    circle1 = Circle.create(5)
    circle2 = Circle.create(10)
    circle3 = Circle.create(15)
    circle4 = Circle.create(20)
end
 
function love.update(dt)
    -- we need tracker/target provide x,y index for Tracker system
    
    -- let mouse provide x,y index
    mouse.x, mouse.y = love.mouse.getPosition()
    
    -- args = speed, tracker, target
    Tracker.trace(8, circle1, mouse)
    Tracker.trace(6, circle2, circle1)
    Tracker.trace(4, circle3, circle2)
    Tracker.trace(2, circle4, circle3)
end
 
function love.draw()
    circle1:draw()
    circle2:draw()
    circle3:draw()
    circle4:draw()
end

conf.lua

function love.conf(t)
    t.title = "Tracker"         -- The title of the window the game is in (string)
    t.author = "bmzz"           -- The author of the game (string)
    t.identity = nil            -- The name of the save directory (string)
    t.version = 072             -- The LOVE version this game was made for (number)
    t.console = false           -- Attach a console (boolean, Windows only)
    t.screen.width = 800        -- The window width (number)
    t.screen.height = 600       -- The window height (number)
    t.screen.fullscreen = false -- Enable fullscreen (boolean)
    t.screen.vsync = true       -- Enable vertical sync (boolean)
    t.screen.fsaa = 0           -- The number of FSAA-buffers (number)
    t.modules.joystick = false  -- Enable the joystick module (boolean)
    t.modules.audio = false     -- Enable the audio module (boolean)
    t.modules.keyboard = false  -- Enable the keyboard module (boolean)
    t.modules.event = true      -- Enable the event module (boolean)
    t.modules.image = false     -- Enable the image module (boolean)
    t.modules.graphics = true   -- Enable the graphics module (boolean)
    t.modules.timer = true      -- Enable the timer module (boolean)
    t.modules.mouse = true      -- Enable the mouse module (boolean)
    t.modules.sound = false     -- Enable the sound module (boolean)
    t.modules.physics = false   -- Enable the physics module (boolean)
end

lua/Circle.lua

 
Circle = {}
Circle.__index = Circle
 
function Circle.create(radius, attributes)
    attrs = attributes or {}
    
    local self = {}
    setmetatable(self, Circle)
    
    self.radius = radius
    self.segments = radius / 2
    if self.segments < 50 then self.segments = 50 end
    
    self.mode = attrs.mode or "line"
    
    self.x = attrs.x or 0
    self.y = attrs.y or 0
    
    self.color = attrs.color or {255, 255, 255}
    
    return self
end
 
function Circle:draw()
    love.graphics.setColor(self.color)
    love.graphics.circle(self.mode, self.x, self.y, self.radius, self.segments)
end

lua/Tracker.lua

 
Tracker = {}
 
function Tracker.trace(speed, tracker, target)
    local diffx = target.x - tracker.x
    local diffy = target.y - tracker.y
    
    local dist = math.sqrt(diffx ^ 2 + diffy ^ 2)
    local perc = speed / dist
    
    if dist > speed then
        tracker.x = tracker.x + diffx * perc
        tracker.y = tracker.y + diffy * perc
    else
        tracker.x = target.x
        tracker.y = target.y
    end
end

2011年5月5日 星期四

用 Tcl 實作輕量級的 Template engine

前言

這個構想來自於 TemplaTcl。

原本是要用在 arm9 上,做為開發網頁的 Template engine,無奈 TemplaTcl 在效能上不盡理想。

這麼簡單的頁面,在 arm 上要跑 0.8 秒…
clip_image002

本來是想針對效能較差的地方做點修改,但程式碼實在是不容易閱讀,且核心演算法也的確有問題(Tcl 不是 C 呀…一個一個字來效能一定差的)…因此便動手自己寫了一套。

 

package 下載

TclTemplate0.0.1.rar

 

安裝方式

與一般 Tcl package 無異,解壓縮至 Tcl/lib 底下即可。

 

使用範例(example1)

header.tmpl
image

footer.tmpl
image

example1.tmpl
image

main.tcl
image

 

OUTPUT is…
image 
image

可以在TclTemplate0.0.1.rar看到更多範例。

 

TODO

  • 可傳遞 dict 型態的資料。

2011年5月4日 星期三

在 LÖVE 實作 Button

前言

絕大部分的應用,都會需要 Button,但在 LÖVE 沒有內建的 Button,因此需要自己實作。

 

範例程式下載

button.love

 

情境說明

左上角建立了三個 Button,都會偵測 hover 事件;上面兩個 Button 按下去時,會在畫面中央印出 Button 上面的文字,而 Clear Button 按下去的時候,將會清除畫面中央的文字。

 

Screenshot

SNAGHTML4aafd2b

 

程式碼(都在範例檔案內)

main.lua

 
require("lua/button.lua")
 
function love.load()
    text = ""
    
    -- 可在建立 Button 時宣告屬性
    buttons = {
        button1 = Button.create("Hello"),
        button2 = Button.create("World", {
            y = 20,
            fontcolor = {255, 0, 0}
        }),
        button3 = Button.create("Clear", {
            y = 40,
            font = love.graphics.newFont(15),
            fontcolor = {0, 255, 0}
        })
    }
    
    buttons.button2.text = "HELLO WORLD" -- 也可隨時調整文字
end
 
function love.update(dt)
    -- 主要畫面更新資訊時,也一併更新所有 button 的資訊
    for dummy, button in pairs(buttons) do
        button:update(dt)
    end
end
 
function love.mousepressed(x, y, button)
    -- 按下滑鼠按鍵時,將資訊傳給 Button:mousepressed 做判斷,可得知是否觸發 press
    if buttons.button1:mousepressed(x, y, button) then
        text = text .. buttons.button1.text .. "\n"
    end
    
    if buttons.button2:mousepressed(x, y, button) then
        text = text .. buttons.button2.text .. "\n"
    end
    
    if buttons.button3:mousepressed(x, y, button) then
        text = ""
    end
end
 
function love.draw()
    -- 繪製主要畫面時,也一併繪製所有的 Button
    for dummy, button in pairs(buttons) do
        button:draw()
    end
    
    love.graphics.setColor(255, 255, 255)
    
    -- 如果 Button hover 狀態為 true,顯示文字在 (600, 0) 的地方
    for dummy, button in pairs(buttons) do
        if button.hover then love.graphics.print(button.text .. " hover", 600, 0) end
    end
    
    love.graphics.print(text, 400, 0)
end

lua/button.lua

 
Button = {}
Button.__index = Button
 
function Button.create(text, attributes)
    attrs = attributes or {}
    
    local button = {}
    setmetatable(button, Button)
    
    -- hover 狀態紀錄,會隨時更新,不可設定
    button.hover = false
    
    -- 可設定的屬性,除 text 外皆有 default 值
    button.text = text
    button.x = attrs.x or 0
    button.y = attrs.y or 0
    
    button.font = attrs.font or love.graphics.newFont(12)
    button.fontcolor = attrs.fontcolor or {255, 255, 255}
    
    return button
end
 
function Button:update(dt)
    -- 動態更新 Button 的寬與高
    self.width = self.font:getWidth(self.text)
    self.height = self.font:getHeight()
    
    -- 抓取滑鼠座標
    local x, y = love.mouse.getPosition()
    
    -- 滑鼠座標落在 Button 內,將 hover 狀態更新為 true 反之為 false
    if x > self.x and x < self.x + self.width and
       y > self.y and y < self.y + self.height then
        self.hover = true
    else
        self.hover = false
    end
end
 
function Button:mousepressed(x, y, button)
    -- hover 狀態為 true,此時偵測到滑鼠按鍵按下,才會回傳 true 反之為 false
    if self.hover then
        return true
    else
        return false
    end
end
 
function Button:draw()
    love.graphics.setFont(self.font)
    love.graphics.setColor(self.fontcolor)
    love.graphics.print(self.text, self.x, self.y)
end

2011年5月3日 星期二

LÖVE 物理引擎實例

前言

LÖVE 內建的物理引擎是基於 Box2D 的,且在 API 的設計上是盡可能與 Box2D 本身的 API 一致的。

 

範例檔案下載

physics.love

 

情境說明

上下左右各建造了 100 公分厚的牆壁,在有重力的環境下,用彈力線懸吊了一塊板子;有顆中心點與滑鼠用彈力線連在一起的球,會隨著滑鼠座標移動…

 

Screenshot

SNAGHTML1dadaae

SNAGHTML1db3607

 

程式碼(都在範例檔案內)

main.lua

function love.load()
    -- 定義物理世界的範圍(左上x, 左上y, 右下x, 右下y)
    world = love.physics.newWorld(-2000, -2000, 2000, 2000)
    -- 700 往下的重力
    world:setGravity(0, 700)
    
    -- 四面牆壁 start
    
    -- 質量宣告為 0 = 無限大 (牆壁)
    borderTop = love.physics.newBody(world, 400, 50, 0)
    top = love.physics.newRectangleShape(borderTop, 0, 0, 800, 100)
    
    -- default 質量為 0
    borderBottom = love.physics.newBody(world, 400, 550)
    bottom = love.physics.newRectangleShape(borderBottom, 0, 0, 800, 100)
    
    borderLeft = love.physics.newBody(world, 50, 300)
    left = love.physics.newRectangleShape(borderLeft, 0, 0, 100, 400)
    
    borderRight = love.physics.newBody(world, 750, 300)
    right = love.physics.newRectangleShape(borderRight, 0, 0, 100, 400)
    
    -- 四面牆壁 end
    
    -- 球 start
    
    circleBody = love.physics.newBody(world, 200, 200)
    
    -- 主體中心點有個半徑 25 的球
    circle = love.physics.newCircleShape(circleBody, 0, 0, 25)
    
    -- 使用內建質量系統(自動依照 Shape 形狀與大小來計算)
    circleBody:setMassFromShapes()
    
    -- 球 end
    
    -- 板子 start
    
    rectangleBody = love.physics.newBody(world, 400, 300)
    rectangle = love.physics.newRectangleShape(rectangleBody, 0, 0, 100, 10)
    rectangleBody:setMassFromShapes()
    
    -- 板子 end
    
    -- 彈力線末端釘在(400, 100),與板子中心點連接在一起(400, 300)
    love.physics.newMouseJoint(rectangleBody, 400, 100)
    
    -- 彈力線末端釘在(200, 200),與球中心點連接在一起(200, 200)
    circleJoint = love.physics.newMouseJoint(circleBody, 200, 200)
end
 
function love.draw()
    -- 畫出球
    love.graphics.circle("line", circleBody:getX(), circleBody:getY(), circle:getRadius(), 50)
    
    -- 畫出板子
    love.graphics.polygon("fill", rectangle:getPoints())
    
    -- 畫出四面牆壁 start
    love.graphics.polygon("fill", top:getBoundingBox())
    love.graphics.polygon("fill", bottom:getBoundingBox())
    love.graphics.polygon("fill", left:getBoundingBox())
    love.graphics.polygon("fill", right:getBoundingBox())
    -- 畫出四面牆壁 end
    
    -- 畫出彈力線末端與板子中心點的連接線
    love.graphics.line(400, 100, rectangleBody:getX(), rectangleBody:getY())
end
 
function love.update(dt)
    -- 物理世界也是要更新訊息的
    world:update(dt)
    
    -- 根據滑鼠座標更改球彈力線的末端(球會跟著滑鼠跑)
    circleJoint:setTarget(love.mouse.getPosition())
end