系列文章目录
前言
🍇一、类(Classes)
1??.类(Classes)
- 转到文本编辑器,创建一个名为classic.lua然后粘贴代码。
- 现在我们必须require它。
function love.load()
Object=require "classic"
end
function love.update(dt)
end
function love.draw()
end
- 现在我们已经准备创建一个类。创建一个名为rectangle.lua,并放入以下代码:
--! file: rectangle.lua
-- Pass Object as first argument.
Rectangle = Object.extend(Object)
function Rectangle.new(self)
self.test = math.random(1, 1000)
end
- 一切都会得到解释。但是首先将这段代码放入main.lua。
--! file: main.lua
function love.load()
Object = require "classic"
--Don't forget to load the file
require "rectangle"
r1 = Rectangle()
r2 = Rectangle()
print(r1.test, r2.test)
end
function love.update(dt)
end
function love.draw()
end
- 当您运行游戏时,您将看到打印了2个随机数。
- 所以让我们一步一步地看这段代码。首先,我们用Rectangle = Object.extend(对象)。这使得Rectangle成为一个Classes 。这将是我们的蓝图。与属性相反,Classes 通常用大写字符编写(因此这将是uppercase或PascalCase)。
- 在main.lua。我们说r1 = Rectangle()。即使Rectangle是一个表,我们仍然可以像调用函数一样调用它。它如何工作的是另一个章节。但是通过调用Rectangle()它创建一个新的实例。这意味着它采用我们的蓝图,创建一个具有所有类特性的新对象。每个新实例都是独一无二的。
- 为了证明这一点r1是唯一的,我们创建另一个名为r2。双方都有属性值 test,但他们的变量不同。
- 当我们调用Rectangle(),它执行调用Rectangle.new。这就是我们所说的构造器。
- 参数self,是我们正在修改的实例。如果我们打字Rectangle.test = math.random(0, 1000),我们将把属性赋予蓝图,而不是用蓝图创建的实例。
- 让我们对我们的类进行一些更改:
--! file: rectangle.lua
Rectangle = Object.extend(Object)
function Rectangle.new(self)
self.x = 100
self.y = 100
self.width = 200
self.height = 150
self.speed = 100
end
function Rectangle.update(self, dt)
self.x = self.x + self.speed * dt
end
function Rectangle.draw(self)
love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
- 这就是移动矩形的对象在Object 组成。除了这次我们将运动和绘图部分的代码放在对象中。现在我们只需要调用update并draw进去main.lua。
--! file: main.lua
function love.load()
Object = require "classic"
--Don't forget to load the file
require "rectangle"
r1 = Rectangle()
r2 = Rectangle()
print(r1.test, r2.test)
end
function love.update(dt)
r1.update(r1,dt)
end
function love.draw()
--love.graphics.print(r1.test,100,100)
--love.graphics.print(r2.test,100,200)
r1.draw(r1,100,200)
end
- 当你运行游戏时,你会看到一个移动的矩形。
- 所以我们创建了一个名为Rectangle()。我们创建了一个名为r1。所以现在r1具有功能update和draw。我们调用这些函数,作为第一个参数,我们传递实例本身r1。这是什么self变成了函数。
- 不过,我们不得不通过,这有点烦人r1每次我们调用它的一个函数。幸运的是,Lua对此有一个简写。当我们使用冒号(:)时,函数调用将自动传递冒号左边的对象作为第一个参数。
--! file: main.lua
function love.load()
Object = require "classic"
--Don't forget to load the file
require "rectangle"
r1 = Rectangle()
r2 = Rectangle()
print(r1.test, r2.test)
end
--[[
function love.update(dt)
r1.update(r1,dt)
end
--]]
function love.update(dt)
r1:update(dt)
end
function love.draw()
--love.graphics.print(r1.test,100,100)
--love.graphics.print(r2.test,100,200)
--r1.draw(r1,100,200)
r1:draw()
end
--! file: rectangle.lua
--Lua turns this into: Object.extend(Object)
Rectangle = Object:extend()
--Lua turns this into: Rectangle.new(self)
function Rectangle:new()
self.x = 100
self.y = 100
self.width = 200
self.height = 150
self.speed = 100
end
--Lua turns this into: Rectangle.update(self, dt)
function Rectangle:update(dt)
self.x = self.x + self.speed * dt
end
--Lua turns this into: Rectangle.draw(self)
function Rectangle:draw()
love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
--Lua turns this into: Object.extend(Object)
Rectangle = Object:extend()
--! file: rectangle.lua
function Rectangle:new(x, y, width, height)
self.x = x
self.y = y
self.width = width
self.height = height
self.speed = 100
end
--Lua turns this into: Rectangle.update(self, dt)
function Rectangle:update(dt)
self.x = self.x + self.speed * dt
end
--Lua turns this into: Rectangle.draw(self)
function Rectangle:draw()
love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
- 有了这个我们可以给r1和r2每个都有自己的位置和大小。
--! file: main.lua
function love.load()
Object = require "classic"
require "rectangle"
r1 = Rectangle(100, 100, 200, 50)
r2 = Rectangle(350, 80, 25, 140)
end
function love.update(dt)
r1:update(dt)
r2:update(dt)
end
function love.draw()
r1:draw()
r2:draw()
end
- 所以现在我们有两个移动的矩形。这就是classes如此厉害的原因。r1和r2都是一样的,但却是独一无二的。
- 另一个让classes如此厉害的原因是继承(inheritance)。
🍈二、继承(inheritance)
1??.继承(inheritance)
- 通过继承,我们可以扩展我们的类。换句话说,我们制作了蓝图的副本,并向其中添加了功能,而没有编辑原始蓝图。
- 假设你有一个怪物游戏。每个怪物都有自己的攻击方式,它们的移动方式不同。但是他们也会受到伤害,甚至死亡。这些重叠的特性应该放在我们称之为超类或者基础类。他们提供了所有怪物都有的特征。然后每个怪物的类可以扩展这个基类,并在其中添加自己的特性。
- 让我们创建另一个移动的形状,一个圆形。我们的移动矩形和圆形有什么共同之处?他们都会搬走。所以让我们为这两个形状创建一个基类。
- 创建一个文件名为shape.lua,并放入以下代码:
--! file: shape.lua
Shape = Object:extend()
function Shape:new(x, y)
self.x = x
self.y = y
self.speed = 100
end
function Shape:update(dt)
self.x = self.x + self.speed * dt
end
--! file: main.lua
function love.load()
Object = require "classic"
require "shape"
require "rectangle"
r1 = Rectangle()
r2 = Rectangle()
end
--! file: rectangle.lua
Rectangle = Shape:extend()
function Rectangle:new(x, y, width, height)
Rectangle.super.new(self, x, y)
self.width = width
self.height = height
end
function Rectangle:draw()
love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
- 用 Rectangle = Shape:extend(),我们使Rectangle继承shape。
- Shape有自己的函数,称为:new()。通过创造Rectangle:new()我们覆盖原始函数。意味着当我们调用Rectangle()它不会执行Shape:new()但是相反的执行Rectangle:new()。
- 但是矩形具有这样的属性super子类,这是一个类Rectangle()类是从Shape继承过来的。用Rectangle.super我们可以访问基类的函数,并用它来调用Shape:new()。
- 我们必须通过self作为第一个参数,不能让Lua用冒号(:)来处理,因为我们没有把函数作为实例来调用。
- 现在我们需要制作一个circle类。创建一个名为circle.lua,并放入下面的代码。
--! file: circle.lua
Circle = Shape:extend()
function Circle:new(x, y, radius)
Circle.super.new(self, x, y)
--A circle doesn't have a width or height. It has a radius.
self.radius = radius
end
function Circle:draw()
love.graphics.circle("line", self.x, self.y, self.radius)
end
- 所以我们使Circle继承Shape。我们给了x和y到new()函数对于Shape用Circle.super.new(self,x,y)。
- 我们给我们的Circle类自己的draw函数。这是你画圆的方法。圆没有宽度和高度,它们有半径。
- 现在在main.lualoadShape.lua和Circle.lua,并改变r2对于Circle。
--! file: main.lua
function love.load()
Object = require "classic"
--Don't forget to load the file
require "shape"
require "rectangle"
--Don't forget to load the file
require "circle"
r1 = Rectangle(100, 100, 200, 50)
--We make r2 a Circle instead of a Rectangle
r2 = Circle(350, 80, 40)
end
function love.update(dt)
r1:update(dt)
r2:update(dt)
end
function love.draw()
r1:draw()
r2:draw()
end
- 现在当你运行游戏时,你会看到一个移动的矩形和一个移动的圆形。
🍊三、代码详细解读
- 让我们再看一遍所有的代码。
- 首先,我们加载library classic 用require “classic”。加载这个库会返回一个表,我们将这个表存储在里面的对象Object。它有模拟一个类所需要的最基本的东西。因为Lua没有类,但是通过使用classic 的我们得到了一个非常好的类的模仿。
- 接下来我们加载shape.lua 。在该文件中,我们创建了一个名为Shape 。我们将使用这个类作为基础类为Rectangle 和Circle 。这些类的两个共同点是它们都有一个x和y变量,并且它水平移动。这些相似之处是我们放进去的变量在Shape 。
- 接下来,我们创建Rectanlge 类。我们使Rectanlge 类继承基类。在里面new()函数,这构造函数,我们调用Rectangle.super.new(self,x,y)。我们用self作为第一个参数,所以Shape 将使用我们蓝图的实例,而不是蓝图本身。我们给矩形一个width 和height 变量,并给它一个绘制函数。
- 接下来我们重复上面的,除了一个圈。所以不是一个宽度和高度我们给它一个radius 变量。
- 既然我们已经把classes 准备好了,我们可以开始做了例子这些classes 。随着 r1 = Rectangle(100, 100, 200, 50) 我们创建了类的一个类Rectanlge 。它是一个由我们的蓝图制成的物体,而不是蓝图本身。我们对此实例所做的任何更改都不会影响该类。我们update 并draw 这个实例,为此我们使用冒号(😃。这是因为我们需要将实例作为第一个参数传递,冒号会让Lua为我们做这件事。
- 最后,我们做同样的事情r2 ,除了我们让它成为一个Circle 。
🍋四、疑惑
- 对于一章来说,这是很多信息,我可以想象如果你很难理解所有这些。如果你是编程新手,你需要一段时间才能理解所有这些新概念,但最终你会习惯的。我会在谈论新主题的同时,不断增加对旧主题的解释。
🍉五、总结
-
Classes 就像蓝图。我们可以从一个类中创建多个对象。为了模拟类,我们使用库classic。您可以用创建一个类ClassName = Object:extend()。您可以使用创建类的实例instanceName = ClassName()。这将调用函数ClassName:new()。这是调用构造函数。类的每个函数都应该以self 开始以便在调用函数时,可以将实例作为第一个参数传递。instanceName.functionName(instanceName)。我们可以使用冒号(:)让Lua替我们做这件事。 -
我们可以用扩展一个类ExtensionName = ClassName:extend()。这使得ExtensionName是ClassName的一份拷贝,以至于我们可以在不改变ClassName的情况下添加变量。如果我们给ExtensionName一种函数ClassName已经有了,我们仍然可以调用原函数用extension name . super . function name(self)。
🍋总结
以上就是今天要讲的内容,本文仅仅简单介绍了Love2d之类和类的继承(Classes And Inheritance),介绍了love2d类和类的继承的使用,与博主的lua语言文章结合更好的理解love2d的编码,如果你是一名独立游戏开发者,或者一位对游戏开发有着深厚兴趣,但是又对于unity3d,ue4等这些对于新手而言不太友好的引擎而头疼的开发者;那么现在,你可以试试Love2D。Love2D是一款基于Lua编写的轻量级游戏框架,尽管官方称呼其为引擎,但实际上它只能称得上是一个框架,因为他并没有一套全面完整的解决方案。不过,这款框架上手及其容易,是学习游戏开发的初学者入门的一个良好选择。
|