热更新之Lua语法基础

lua与C#不同,它是解析型语言,弱类型数据

一.HelloWorld与注释

HelloWorld演示
-- 分号可写可不写
-- 用于打印Lua内容的语法
print("Hello world!")
注释演示
-- 这是Lua的单行注释

--[[
	多行注释不可嵌套
	这是Lua的多行注释
]]

--[[
	这也是Lua的多行注释
	为了好看
--]]
对比
书写C#Lua
函数关系有明确的所属类一个独立的方法, 无明确的所属类
代码结尾必须以;结尾,否则语法错误不需要结尾符号,或者以;号结尾
命名规则方法和类都是帕萨卡命名法全小写?
代码注释C#Lua
单行注释//
多行注释/* */–[[ --]],–[[ ]]
文档注释///没有

二.变量与基本数据类型

变量演示
-- 变量声明,不需要提供类型
-- 变量名 = 变量值
-- 轻易不要使用全局变量,因为它可以被随意访问,数据容易被恶意篡改
name = "hxsd"

print(name)

-- 系统内置变量
-- 下划线加大写字母
print(_VERSION)

-- 可以调用一个从未声明的变量
-- nil等同于C#的null
print(id)

-- 销毁变量
-- 赋值为nil,变量就会销毁
name = nil
print(name)

-- C#的变量作用域
-- 局部变量,成员变量

-- 变量作用域
-- 1.当前文件
-- 2.当前方法(函数)
-- 3.系统全局(整个编译器下都有效)

-- 这个变量是一个局部变量
-- 当前文件有效
local data = "hxsd"
print(data)

-- 同时定义多个变量
local v1, v2 = "hehe", "haha"
print(v1)
print(v2)
基本数据类型演示
local name = "Unity"

-- 获得变量数据类型
-- type()是一个Lua的内置函数,不属于任何类或对象的,可复用的代码片段
print(type(name))

-- type的返回值类型是string
print(type(type(name)))

-- 判定name的类型是字符串
print(type(name) == "string")

-- 对没有声明的变量type
print(type(id))

-- 判定一个变量是否定义
print(type(id) == "nil")
print(id == nil)

-- Lua的数字只有number类型
print(type(123))
print(type(3.14))

-- 布尔型(boolean)
print(type(true))
print(type(false))

-- 单引号字符串
print(type('hxsd'))

-- 一切复杂数据皆表(table)
-- C#对象,结构体
-- C#数组,列表
-- C#字典

print({})
print(type({}))

-- 母体语言中定义的数据类型,传递给Lua(userdata)
对比
变量C#Lua
语法格式数据类型 变量名 = 值变量名 = 值
命名规则驼峰命名法全小写?
基本数据类型C#Lua
类型区别强类型语言,声明变量必须要明确的指定数据类型弱类型语言,声明变量时不需要指定类型,变量的类型由变量内存储的数据决定
数值类型int,float,doublenumber
布尔类型boolboolean
字符串类型必须使用双引号包裹可以使用双引号,也可以使用单引号
char类型有char类型,单引号包裹没有

三.运算符

运算符演示
-- 数字的几次方
print(2 ^ 3)

-- 不等于
print(2 ~= 3)

-- 没有++ --,也没有+= -= *= /=
local i = 0
i = i + 1

print(i)
对比
常用运算符C#Lua
复合赋值运算符+= -= *= /= %= ++ –没有
关系运算符不等于!=不等于~=
逻辑运算符&& || !and or not
+数学意义上的相加;字符串相连只是数学意义上的相加;字符串相连用”…“[两个点]

四.分支结构与循环结构

分支结构演示
--[[
	if(条件)
	{
		条件满足时,执行的代码
	}
]]

if true
then
	print("进入if")
end

--[[
	if(条件)
	{
		if条件满足时,执行的代码
	}
	else
	{
		if条件不满足时,执行的代码
	}
]]

if false
then
	print("进入if")
else
	print("进入else")
end

--[[
	if(条件)
	{
		if条件满足时,执行的代码
	}
	elseif(条件)
	{
		if条件不满足,elseif条件满足时,执行的代码
	}
	else
	{
		if条件不满足,elseif条件也不满足时,执行代码
	}
]]

-- 在关键字中,有if就应该写then
if false
then
	print("进入if")
elseif true
then
	print("进入elseif")
else
	print("进入else")
end

-- if嵌套
if true
then
	if false
	then
		print("进入第二层if")
	else
		print("进入第二层else")
	end
end

-- 没有switch,只能使用if-elseif-else编写分支代码

循环结构演示
--[[
	当条件满足时,进入循环体,执行一次内部代码

	while(条件)
	{
		循环体
	}
]]

-- 执行3次循环,并在内部输入变量的值

-- 分支用then,循环用do

local num = 1

while num < 4 -- 当条件满足时,进入循环体
do
	-- local修饰num,num是局部变量,num的作用域是整个当前文件,所以while内部可以取到num变量的值
	print(num)
	num = num + 1
end

--[[

	先执行一次do结构中的代码
	判定while中的条件是否满足
	若条件满足,再次执行do结构中的代码
	若条件不满足,结束循环

	do
	{
		循环体
	}
	while(条件)
]]

local num = 1

repeat
	print(num)
	num = num + 1
until num > 3 -- 直到条件满足时,结束循环(和do-while相反)

--[[
	do
	{
		if(条件1)
		{
			逻辑代码
			break
		}

		if(条件2)
		{
			逻辑代码
			break
		}
	}
	while(false)
]]

-- Lua老司机
-- Lua中没有continue,无法跳过当前这次循环

repeat
	if false
	then
		print("执行第一个分支")
		break
	end

	if true
	then
		print("执行第二个分支")
		break
	end

	if true
	then
		print("执行第三个分支")
	end
until true


-- for循环,循环指定的次数

--[[
	for(int i = 0; i < 10; i++)
	{

	}
]]

-- 参数1:计数变量的初始值
-- 参数2:增长到多少
-- 参数3:增长步长,默认是1(i每次递增几)
for i = 1, 10, 1
do
	print(i)
end

print("----------------")

-- 递减
for i = 9, 1, -1
do
	print(i)
end

print("----------------")

local data = {"a", "b", "c", "d", "e"}

for i = 1, #data, 1
do
	print(data[i])
end

print("----------------")

for i = #data, 1, -1
do
	print(data[i])
end

对比
分支结构C#Lua
if语句代码体部分用一个{ }进行包裹用then和end代替{ }进行包裹
if…else…语句if和else下方都跟着一个代码体,每个代码体都需要用{ }包裹只有在if语句下方才会出现then,整个if代码的结尾处用end
if…else if…语句else if两个单词之间是有空格的elseif两个单词相连且没有空格
switch…case…语句没有
循环结构C#Lua
while语句代码体部分用{ }包裹代码体部分用do和end包裹
do…while语句无,但有类似的语句;布尔表达式不成立的时候才会继续循环
for语句参数之间用;分号分割参数之间用,逗号分割
关键字有break和continue只有break

五.数组

数组演示
-- 数组实际上就是表,现阶段只考虑数字索引

-- C# string[] data = new string[20];

local data = {}

-- 下标是从1下标开始
-- 内部存储类型可以混合
-- 索引可以是负数
-- 虽然下标是从1开始,但是可以有0索引
-- 索引可以有间隔
-- 没有提供索引的数据,下标是从1开始
-- [索引] = 值方式,可以指定数据的下标
data = {"abc", 123, [-1] = 100, [0] = true, [4] = 233}

-- 通过下标增加值
data[-2] = "def"

print(data[1])
print(data[2])
print(data[-2])
print(data[-1])
print(data[0])
print(data[4])

-- 获取table长度
-- 计算table是从1下标开始的,根据连续索引计数,中间有断裂,则计数结束
-- !!!这种计数不靠谱!!!
print("长度是:" .. #data)
--getn获取长度
for i=1,table.getn(data ),1 do
print(data[i])
end
-- 通过下标修改值,也可以修改类型
data[0] = 3.14
print(data[0])

-- 多维table
local multi = {
	{"abc", 123},
	{true, {"hehe"}}
}
print(multi[2][1])
print(multi[2][2][1])

对比
数组C#Lua
长度长度是固定的长度不固定,可以给后续的索引位置继续赋值
下标从0开始从1开始
类型只能存储一种类型可以存储多种类型
声明数组string[] data={"baidu","QQ","JD"};data={"baidu","QQ","JD"}
数组长度数组本身的属性data.Length需要用到table中的方法table.getn(data)

六.函数

函数演示
-- 解释型语言代码都是自上而下进行解析,所以函数需要先定义,才可以调用

-- 编译型语言
-- 编写阶段:不分声明,执行先后
-- 运行阶段:编译器会将声明编译在调用之前,声明先于调用

-- 解释型语言
-- 编写阶段:声明写在调用之前
-- 运行阶段:声明在调用之前运行

-- 调用失败
-- func1()

function func1()
	print("这是func1")
end

func1()

-----------------------------------------

-- function是存储在变量中的
-- 可以理解为C#的委托
local func2 = function()
	print("这是func2")
end

func2()

-----------------------------------------

local func3 = function(a, b)
	print("这是func3输出:" .. a + b)
end

func3(1, 3)
-- 参数的数量可以多于函数提供的参数,不能少于参数数量
func3(1, 3, 7)

-----------------------------------------

-- 可变参数(无固定数量)
-- 无论提供多少参数,全部进行相加
local func4 = function(...)
	-- 无固定参数,需要转换为table
	-- arg的作用域是当前函数体
	local arg = {...}

	local total = 0

	-- 下划线将下标的值丢弃了
	for _, v in pairs(arg)
	do
		total = total + v
	end

	print("这是func4输出:" .. total)
end

func4(1, 3)
func4(1, 3, 7)
func4(1, 1, 1, 1)

-----------------------------------------

function func5()
	-- 多个返回值之间用逗号分隔
	return 99, 999
end

-- local 修饰两个变量,表示他们两个作用域是当前文件
local num1, num2 = func5()
print(num1)
print(num2)


对比
函数C#Lua
语法格式static void Hello(){Console.WriteLine("Monkey");}function Hello( )print("Monkey")end
代码顺序编译型语言,代码顺序无所谓解析型语言,必须先声明,然后才可以调用
函数作为参数传递适用委托实现没有委托的概念,真的可以直接当成参数

七.作用域与字符串

作用域演示
--全局变量.
 name = "Monkey"
 age = 100

--局部函数.private
local function Hello()
	--全局变量.
	local webName = "得到APP"
	print(webName .. "方法内的")
	print(name, age)
end

--print(name)
--print(age)
--print(name, age)
Hello()
print(webName)
--\转义符
str4 = "My \\Name \"Is\' \nMonkey!"
--print(str4)

字符串演示
-- 声明单行字符串
local str1 = 'aBc'
local str2 = "def"

-- 多行字符串声明
-- 每一次换行都会插入一个换行符"\n"
local str3 = [[
ghi
jkl
]]

print(str3)

-- 字符串拼接
print(str1 .. str2)

-- 这样写会报错
-- print(str1 + str2)

-- Lua会将字符串尝试转换为数字,再相加
-- 转换字符串失败,加的结果报错
-- 转换数字成功,那么结果是两个数字相加的结果
print("6" + "7")

-- Lua获取字符串长度
print(#str1)

------------------------华丽的分割线----------------------------------

-- 字符串函数使用
-- string.函数名()

-- 大写化
print(string.upper(str1))
-- 小写化
print(string.lower(str1))
-- 字符串反转
print(string.reverse(str1))

-- Lua支持多返回值
-- Lua的字符串是以下标1开始的

-- 字符串查找(KMP算法)
-- 参数1:被查找的字符串
-- 参数2:要查找的内容
-- 返回值(多返回值):起始位置和结束位置
print(string.find("abcdef", "cde"))

-- 字符串截取
-- 参数1:被截取的字符串
-- 参数2:截取的起始下标
print(string.sub("abcdef", 3))

-- 字符串截取
-- 参数1:被截取的字符串
-- 参数2:截取的起始下标
-- 参数3:截取的结束下标
print(string.sub("abcdef", 4, 5))

-- 截取到倒数第二个字符
local str4 = "abcdefghijklmn"
print(string.sub(str4, 3, #str4 - 1))

-- 字符串格式化
-- %s,%d是占位符,表示此处会显示一个数据,%s表示字符串,%d表示数字 %f 表示小数
print(string.format("Im a Player %s %s, level is %d", "Carol","luosi", 99));

-- 字符串重复
-- 参数1:需要重复的串
-- 参数2:需要重复的次数
print(string.rep("abc", 3))

-- 字符串修改
-- 参数1:需要替换的串
-- 参数2:被替换的内容
-- 参数3:替换后的内容
print(string.gsub("abcdefg", "cd", "**"))

-- 字符转ascii码
print(string.byte("A"))
-- ascii码转字符
print(string.char(65))

八.table表与模块

table表演示
array = {1, 2, 3, 4, 5}
print(type(array))

print("-----------table基本使用[数组方式]--------------")

--<1>初始化table
myTable = {}

--<2>给table 赋值
myTable[1] = "baidu"
myTable[2] = "taobao"
myTable[3] = "jd"

--<3>在table中取值
print(myTable[1])

print("-----------table基本使用[键值对方式]--------------")

--<1>初始化table
myTable2 = {}

--<2>给table 赋值
myTable2['baidu'] = "李彦宏,www.baidu.com"
myTable2['360'] = "周鸿祎,www.360.cn"

--<3>在table中取值
print(myTable2['360'])

--<1>增加元素
--table.insert(表名, [位置], 值)
table.insert(myTable, 1, "dedaoapp")
--table.insert(myTable2, 3, "马化腾,www.qq.com")
myTable2['qq'] = "马化腾,www.qq.com"

--<2>移除元素
--table.remove(表名, [位置])
table.remove(myTable, 1)
--table.remove(myTable2, 1)
myTable2['360'] = nil

print("数组模式:" .. table.getn(myTable))


print("-----------迭代器遍历table[数组方式]--------------")

for key, value in ipairs(myTable) do
	print(key, value)
end

print("-----------迭代器遍历table[键值对方式]--------------")

index = 0
for key, value in pairs(myTable2) do
	print(key, value)
	index = index + 1
end

print("键值对模式:" .. index)


-- 可以将Lua中的Table理解为一个对象

-- one,two相当于字符串下标
-- one,two没有区别,都代表成员变量
-- one,two类似于字典
local data = {"aa", "bb", one = "cc", [4] = 3, ["two"] = "dd"}

print(data[2])
-- 理解为字典
print(data["one"])
-- 理解为对象
print(data.two)

-- 因为function是一种数据类型
-- data内部有func1下标,所以就相当于将函数存储在了data表中,也就是成员方法
data.func1 = function()
	print("来自于data的func1方法")
end

data.func1()

-------------------------------------

data.func2 = function()
	-- 为什么可以通过data来获取one索引的值?
	-- 因为data的作用域为当前文件
	print("来自于data的func2方法,获取one索引的值:" .. data.one)
end

data.func2()

-------------------------------------

--[[
	public class Test
	{
		public int ID;

		public void Show()
		{
			//C#为了开发方便,隐式的添加了"this."
			ID = 99;
			//通过this获得当前对象,并给其成员变量ID赋值为100
			this.ID = 100;
		}
	}

	var obj = new Test();
	obj.Show();
]]

-- 因为func3定义在data表内部,所以现在希望通过一种方式,直接获得data对象,而不是通过文件全局变量获得
-- self关键字:self等同于C#的this,表示当前对象
-- 成员方法内部可以通过self获得当前表的其他数据
data.func3 = function(self)
	print("来自于data的func3方法,获取one索引的值:" .. self.one)
end

-- self需要表,则将data传递过去
data.func3(data)

-- 语法糖
-- 冒号表示隐式传递data到func3方法的self变量
data:func3()

-------------------------------------

-- 隐式的声明了一个self参数
function data:func4()
	print("来自于data的func4方法,获取one索引的值:" .. self.one)
end

-- 显式传递表
data.func4(data)
-- 隐式传递表
data:func4()

-- Lua中冒号和点的区别
-- 声明时:使用点声明方法,如果在内部需要调用成员变量,则需要使用self,那么方法也需要声明self参数,而冒号会帮开发者隐式声明
-- 调用时:点相当于成员变量的方式调用,如果声明的方法上有self参数,则需要显示的传递当前表,而冒号会帮开发者隐式传递

模块演示
-- 只存在与脚本语言中
-- 脚本语言自上而下执行
-- 脚本和脚本之间是不同的文件

-- 一个脚本中,执行另一个脚本中的代码

-- 文件加载:require("文件路径")
-- 文件名不加扩展名

-------------------------------------------

-- 执行call.lua的代码
require("Subfile/call")

-------------------------------------------

-- 全局变量的获取
require("Subfile/data")
print(data[1])

-------------------------------------------

-- 局部变量返回给另一个文件
local config = require("Subfile/config")
print(config.Role)

-------------------------------------------

-- 预加载目录
print(package.path)
-- "./"表示获得当前文件所在目录下的文件
-- 这个路径和上面的路径不一样,因为多了./所以会执行两次
require("./Subfile/call")

-------------------------------------------

-- 多次执行一个文件的内部代码
-- 所有被加载过的文件,路径信息都会记录在package.loaded,当再次加载时会判定这个信息,如果存在则不会再次加载
print(package.loaded["Subfile/call"])

-- 虽然之前加载过这个文件,但是这样可以保证被加载的文件会再次执行
package.loaded["Subfile/call"] = nil
require("Subfile/call")

九.元表

元表演示
-- 后续的学习教程:https://www.runoob.com/lua/lua-metatables.html

local t1 = {1, 2, 3, ["aa"] = true}
local t2 = {4, 5, 6}

-- 直接打印table,显示的是内存地址
-- 期望打印的是"{1, 2, 3}",便于程序员阅读
-- 现在就需要扩展t1功能,实现打印便于人类阅读,扩展table使用的就是metatable语法特性
print(t1)

-- 元表是一个table,但是这个table有特定的写法
-- 需求:打印利于阅读的内容,实际上将表转换为string类型

local meta = {
	-- t是被扩展的表
	__tostring = function(t)
		local format = "{"

		for k, v in pairs(t)
		do
			format = format .. k .. " = " .. tostring(v) .. ", "
		end

		--把多于的逗号截掉
		format = string.sub(format, 1, #format - 2)
		format = format .. "}"

		return format;
	end
}

-- 将meta设置为t1的功能扩展表(元表)
setmetatable(t1, meta)
-- 将t1当成字符串进行打印
-- 如果t1设置了元表,并且元表中实现了__tostring方法,则自动调用元表的__tostring方法
-- 白话:t1本身不支持变字符串,元表给t1扩展了变字符串功能。让t1变字符串,就会调用这个扩展功能
print(t1)

setmetatable(t2, meta)
print(t2)

--[[
tableA = {} --表A [主表]
tableB = {} --表B [元表/子表]

setmetatable(tableA, tableB) -- tableB就是tableA的元表.

print(getmetatable(tableA)) -- 判断tableA是否有元表

--]]


tableA = {name = "Carol", age = 100}
tableB = {gender = "男", address = "湖南常德"}

setmetatable(tableA, tableB)
tableB.__index = tableB -- 设置元表的__index索引.

print(tableA.name, tableA.age)
--print(tableA["name"])
print(tableA.gender, tableA.address)


总结

Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:

1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。

十.类与继承

类演示
--[[
str1 = "Monkey"
str2 = "Monkey"

tableA = {111, 222, 333}
tableB = {111, 222, 333}
tableC = tableA

print(str1 == str2) --
print(tableA == tableC) --
--]]


--初始化了一个类.
Person = {name, age, gender, address = "中国"} --在Person类中模拟字段.

--模拟构造方法.
function Person:New()
	obj = {} --初始化一个新的表.
	setmetatable(obj, Person) --把当前的类[表]设置为新表的元表.
	Person.__index = Person --指定元表的__index索引.
	return obj
end


--模拟一个方法.
function Person:Show()
	print(self.name, self.age, self.gender, self.address)
end

--实例化对象.
lkk = Person:New()
lkk.name = "张三"
lkk.age = 110
lkk.gender = "男"
lkk.address = "第十一区"
lkk:Show()

mk = Person:New()
mk.name = "Monkey"
mk.age = 10
mk.gender = "男"
mk:Show()

print(lkk == mk)


属性成员演示

--创建一个Hero类.
Hero = {name, hp, mp, attack} --定义相关字段.

--定义构造方法.
function Hero:New(name, hp, mp, attack)
	local obj = {}
	--setmetatable(obj, Hero)
	--Hero.__index = Hero
	setmetatable(obj, self)
	self.__index = self
	self.name = name
	self.hp = hp
	self.mp = mp
	self.attack = attack
	return obj
end

--定义相关方法.
function Hero:Attack1()
	print(self.name .. "攻击方法一执行啦..")
end

function Hero:Attack2()
	print(self.name .. "攻击方法二执行啦..")
end

function Hero:ToString()
	print(string.format("角色名:%s,生命值:%s,法力值:%s,攻击力:%s", self.name, self.hp, self.mp, self.attack))
end


--实例化对象.
--mk = Hero:New()
--mk.name = "mkcode"
--mk.hp = 1000
--mk.mp = 500
--mk.attack = 100

mk = Hero:New("Carol", 1999, 1888, 1777)
mk.hp = 1000000
mk:Attack1()
mk:Attack2()
mk:ToString()

继承演示

--创建一个动物类.
Animal = {name} --字段.

--构造方法.
function Animal:New(name)
	local obj = {}
	setmetatable(obj, self)
	self.__index = self
	self.name = name
	return obj
end

--普通方法.
function Animal:ToString()
	print(self.name .. "Animal类中的方法")
end

--子类继承父类.
Cat = Animal:New(nil)

--子类的构造方法.
function Cat:New(name)
	local obj = Animal:New(name)
	setmetatable(obj, self)
	self.__index = self
	--self.name = name
	return obj
end

--子类当的普通方法.
function Cat:Eat(food)
	print(self.name .. "吃:" .. food)
end


--[[
--创建对象.
jfm = Animal:New("加菲猫")
jfm:ToString()
print(jfm.name)
--]]

--通过子类实例化对象.
jfm = Cat:New("加菲猫")
jfm:ToString()
jfm:Eat("鱼")
print(jfm.name)

tom = Cat:New("汤姆")
tom:ToString()
tom:Eat("杰瑞")
print(tom.name)

参考资料:
擅码网Unity3D中级课程之Lua热更新
火星时代Unity课程
Lua 教程

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值