package.path = package.path .. ";E:\\Lua\\lualogging\\src\\?.lua"
require("logging")
require("logging.file")
function GetMicroTime()
return love.timer.getTime() * 1000
end
-- This function is called exactly once at the beginning of the game.
function love.load()
-- Init log
logger = logging.file("Snake_%s.log", "%Y-%m-%d")
logger:info("logging.file test")
logger:debug("debug...")
logger:error("error!")
logger:info("Game Start.")
-- Init images
images = {}
for idx, image in ipairs({'blank', 'body'}) do
images[image] = love.graphics.newImage('images/' .. image .. '.png')
end
logger:info(images)
-- Init Windows Size
cellSize = 7
gridXCount = 50
gridYCount = 50
WindowWidth = cellSize * gridXCount
WindowHeight = cellSize * gridYCount
logger:info("WindowWidth = " .. WindowWidth .. " WindowHeight = " .. WindowHeight)
-- Init Speed, 1000ms one move.
SnakeSpeed = 200
LastTime = GetMicroTime()
function GetOneBlankPosition()
local BlankPosition = {}
for x = 1, gridXCount do
for y = 1, gridYCount do
if grid[x][y].type == 0 then
table.insert(BlankPosition, {x, y})
end
end
end
return BlankPosition[love.math.random(#BlankPosition)]
end
function ShowGrid()
for x = 1, gridXCount do
local rowStr = string.format("%02d ", x)
for y = 1, gridYCount do
rowStr = rowStr .. grid[x][y].type .. " "
end
logger:info(rowStr)
end
end
-- Init Grid
function reset()
logger:info("reset")
grid = {}
for x = 1, gridXCount do
grid[x] = {}
for y = 1, gridYCount do
grid[x][y] = {
type = 0 -- 0:blank, 1:Snake Body, 2:Food
}
end
end
local RandX = love.math.random(5, gridXCount - 4)
local RandY = love.math.random(5, gridYCount - 4)
HeadDirection = love.math.random(4) -- 1:Up, 2:Right, 3:Down, 4:Left
local TailX = RandX
local TailY = RandY
if HeadDirection == 1 then
TailY = TailY + 1
elseif HeadDirection == 2 then
TailX = TailX - 1
elseif HeadDirection == 3 then
TailY = TailY - 1
else
TailX = TailX + 1
end
grid[RandX][RandY].type = 1
grid[TailX][TailY].type = 1
SnakeList = {}
table.insert(SnakeList, {RandX, RandY})
table.insert(SnakeList, {TailX, TailY})
FoodPosition = GetOneBlankPosition()
grid[FoodPosition[1]][FoodPosition[2]].type = 2
ShowGrid()
end
reset()
end
-- Callback function used to update the state of the game every frame.
function love.update()
currentTime = GetMicroTime()
-- logger:info("currentTime = "..currentTime.." LastTime = "..LastTime)
if currentTime - LastTime < SnakeSpeed then
return
end
LastTime = currentTime
local tail = SnakeList[1]
local tailX = tail[1]
local tailY = tail[2]
local head = SnakeList[#SnakeList]
local headX = head[1]
local headY = head[2]
local newNodeX = headX
local newNodeY = headY
if HeadDirection == 1 then
newNodeX = newNodeX - 1
elseif HeadDirection == 2 then
newNodeY = newNodeY + 1
elseif HeadDirection == 3 then
newNodeX = newNodeX + 1
else
newNodeY = newNodeY - 1
end
logger:info("HeadDirection = "..HeadDirection)
logger:info("tailX = "..tailX.." tailY = "..tailY)
logger:info("headX = "..headX.." headY = "..headY)
logger:info("newNodeX = "..newNodeX.." newNodeY = "..newNodeY.." type = "..grid[newNodeX][newNodeY].type)
if grid[newNodeX][newNodeY].type == 0 then
grid[tailX][tailY].type = 0
grid[newNodeX][newNodeY].type = 1
table.remove(SnakeList, 1)
table.insert(SnakeList, {newNodeX, newNodeY})
elseif grid[newNodeX][newNodeY].type == 1 then
-- reset()
elseif grid[newNodeX][newNodeY].type == 2 then
grid[newNodeX][newNodeY].type = 1
table.insert(SnakeList, {newNodeX, newNodeY})
FoodPosition = GetOneBlankPosition()
grid[FoodPosition[1]][FoodPosition[2]].type = 2
end
end
-- Callback function used to draw on the screen every frame.
function love.draw()
local function drawCell(image, x, y)
love.graphics.draw(image, (y-1)*cellSize, (x-1)*cellSize)
end
for x = 1, gridXCount do
for y = 1, gridYCount do
if grid[x][y].type == 0 then
drawCell(images.blank, x, y)
else
drawCell(images.body, x, y)
end
end
end
end
function love.keypressed(key)
logger:info("key = " .. key)
if key == 'w' and HeadDirection ~= 3 then
HeadDirection = 1
elseif key == 'd' and HeadDirection ~= 4 then
HeadDirection = 2
elseif key == 's' and HeadDirection ~= 1 then
HeadDirection = 3
elseif key == 'a' and HeadDirection ~= 2 then
HeadDirection = 4
elseif key == 'space' then
SnakeSpeed = 100
else
logger:info("Unknown key = " .. key)
return
end
end
function love.keyreleased(key)
if key == 'space' then
SnakeSpeed = 200
end
end
如果是一个 *.LUA 的文件, 里面用到了自己写的库, 或者第三方写的库, 但是你不想把它放到 lua 的安装目录里, 则在代码里面可以指定require搜索的路径。
package.path = '/usr/local/share/lua/5.1/?.lua;/home/resty/?.lua;' --搜索lua模块
package.cpath = '/usr/local/lib/lua/5.1/?.so;' --搜索so模块
package.path = package.path .. ";E:\\Lua\\lualogging\\src\\?.lua"
https://github.com/Neopallium/lualogging
日志库就是 lualogging,放在 E:\Lua 这个目录下面。
package.path = package.path .. ";E:\\Lua\\lualogging\\src\\?.lua"
require("logging")
local logger = logging.new(
function(self, level, message)
print(level, message)
return true
end
)
-- logger:setLevel(logging.WARN)
logger:log(logging.INFO, "sending email")
logger:info("info log.")
logger:warn("warn log.")
logger:error("error log.")
local tab = { a = 1, b = 2, c = {1,2,3,4,5,6}}
logger:debug(tab)
logger:info("val1 = '%s', val2 = %d", "string value", 1234)
package.path = package.path .. ";E:\\Lua\\lualogging\\src\\?.lua"
require("logging")
require("logging.file")
local logger = logging.file("test_%s.log", "%Y-%m-%d")
logger:info("logging.file test")
logger:debug("debug...")
logger:error("error!")
比如,我们有两个分数:
fraction_a = {numerator=2, denominator=3}
fraction_b = {numerator=4, denominator=7}
我们想实现分数间的相加:2/3 + 4/7,我们如果要执行: fraction_a + fraction_b,会报错的。
所以,我们可以动用MetaTable,如下所示:
fraction_op={}
function fraction_op.__add(f1, f2)
ret = {}
ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator
ret.denominator = f1.denominator * f2.denominator
return ret
end
为之前定义的两个table设置MetaTable:(其中的setmetatble是库函数)
setmetatable(fraction_a, fraction_op)
setmetatable(fraction_b, fraction_op)
于是你就可以这样干了:(调用的是fraction_op.__add()函数)
fraction_s = fraction_a + fraction_b
至于__add这是MetaMethod,这是Lua内建约定的,其它的还有如下的MetaMethod:
__add(a, b) 对应表达式 a + b
__sub(a, b) 对应表达式 a - b
__mul(a, b) 对应表达式 a * b
__div(a, b) 对应表达式 a / b
__mod(a, b) 对应表达式 a % b
__pow(a, b) 对应表达式 a ^ b
__unm(a) 对应表达式 -a
__concat(a, b) 对应表达式 a .. b
__len(a) 对应表达式 #a
__eq(a, b) 对应表达式 a == b
__lt(a, b) 对应表达式 a < b
__le(a, b) 对应表达式 a <= b
__index(a, b) 对应表达式 a.b
__newindex(a, b, c) 对应表达式 a.b = c
__call(a, ...) 对应表达式 a(...)
Set = {}
Set.mt = {} -- metatable for sets
function Set.new(t)
t = t or {}
local set = {}
setmetatable(set, Set.mt)
for _, l in ipairs(t) do
set[l] = true
end
return set
end
function Set.union(a, b)
if getmetatable(a) ~= Set.mt or
getmetatable(b) ~= Set.mt then
error("Attempt to 'add' a set with a not-set value", 2)
end
local res = Set.new()
for k in pairs(a) do
res[k] = true
end
for k in pairs(b) do
res[k] = true
end
return res
end
function Set.intersection(a, b)
local res = Set.new()
for k in pairs(a) do
res[k] = b[k]
end
return res
end
function Set.tostring(set)
set = set or {}
local s = "{"
local sep = ""
for e in pairs(set) do
s = s .. sep .. e
sep = ", "
end
return s .. "}"
end
function Set.print(s)
print(Set.tostring(s))
end
-- 相加
Set.mt.__add = Set.union
-- 相乘,交集
Set.mt.__mul = Set.intersection
s1 = Set.new({10, 20, 30, 50})
s2 = Set.new({30, 1})
print(getmetatable(s1))
print(getmetatable(s2))
Set.print(s1)
Set.print(s2)
s3 = s1 + s2
Set.print(s3)
s = Set.new({1, 2, 3})
-- s = s + 8 -- table加上一个数字
Set.print(s1 * s2)
print("--------------")
print(s1)
print(s2)
-- print会自动调用__tostring,我们在这里修改了__tostring
Set.mt.__tostring = Set.tostring
print(s1)
print(s2)
参考链接:
读书笔记: Programming in Lua, 4th Edition.
> string.format("%x", 0xff & 0xabcd) --> cd
> string.format("%x", 0xff | 0xabcd) --> abff
> string.format("%x", 0xaaaa ~ -1) --> ffffffffffff5555
> string.format("%x", ~0) --> ffffffffffffffff
Negative displacements shift in the other direction, that is, a » n is the same as a « -n:
> string.format("%x", 0xff << 12) --> ff000
> string.format("%x", 0xff >> -12) --> ff000
If the displacement is equal to or larger than the number of bits in the integer representation (64 in Standard Lua, 32 in Small Lua), the result is zero, as all bits are shifted out of the result:
> string.format("%x", -1 << 80) --> 0
读书笔记: Programming in Lua, 4th Edition.
Account = {
balance = 0,
withdraw = function(self, v)
self.balance = self.balance - v
end
}
function Account:deposit(v)
self.balance = self.balance + v
end
-- 用.来调用函数的话,需要手动给self传值。
-- 用冒号来调用函数的话,可以省略self。
Account.deposit(Account, 200)
Account:deposit(200)
Account.withdraw(Account, 100)
Account:withdraw(100)
在上面的例子中,Account只是一个对象实例。
在C++中,一个类,可以生成多个对象实例。
在Lua中,没有类的概念,Lua是用metatable来模拟实现类的。
if we have two objects A and B, all we have to do to make B a prototype for A is this:
setmetatable(A, {__index = B})
After that, A looks up in B for any operation that it does not have.
Let us go back to our example of a bank account. To create other accounts with behavior similar to Account, we arrange for these new objects to inherit their operations from Account, using the __index metamethod.
local mt = {__index = Account}
function Account.new (o)
o = o or {} -- create table if user does not provide one
setmetatable(o, mt)
return o
end
After this code, what happens when we create a new account and call a method on it, like this?
a = Account.new{balance = 0}
a:deposit(100.00)
When we create the new account, a, it will have mt as its metatable. When we call a:deposit(100.00), we are actually calling a.deposit(a, 100.00); the colon is only syntactic sugar. However, Lua cannot find a “deposit” entry in the table a; hence, Lua looks into the __index entry of the metatable. The situation now is more or less like this:
getmetatable(a).__index.deposit(a, 100.00)
The metatable of a is mt, and mt.__index is Account. Therefore, the previous expression evaluates to this one:
Account.deposit(a, 100.00)
That is, Lua calls the original deposit function, but passing a as the self parameter. So, the new account a inherited the function deposit from Account. By the same mechanism, it inherits all fields from Account.
We can make two small improvements on this scheme. The first one is that we do not need to create a new table for the metatable role; instead, we can use the Account table itself for that purpose. The second one is that we can use the colon syntax for the new method, too. With these two changes, method new becomes like this:
function Account:new (o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
Now, when we call Account:new(), the hidden parameter self gets Account as its value, we make Account.__index also equal to Account, and set Account as the metatable for the new object. It may seem that we do not gained much with the second change (the colon syntax); the advantage of using self will become apparent when we introduce class inheritance, in the next section.
Account = {balance = 0}
function Account:new(o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
function Account:deposit(v)
self.balance = self.balance + v
end
function Account:withdraw(v)
if v > self.balance then error"insufficient funds" end
self.balance = self.balance - v
end
SpecialAccount = Account:new()
s = SpecialAccount:new{limit = 1000.00}
function SpecialAccount:withdraw(v)
if v - self.balance >= self.getLimit() then
error"insufficient funds"
end
self.balance = self.balance - v
end
function SpecialAccount:getLimit()
return self.limit or 0
end
就像一个链表一样,通过metatable一层一层向上找。