Mục lục
Lấy biến Trigger bằng cách sử dụng script
Danh sách thuộc tính hành động (ActionAttr)
Tên (Name) | Giá trị (Value) | Mô tả (Description) |
Position | 1 | Vị trí |
AreaIns | 2 | Khu vực |
Number | 3 | Giá trị |
String | 4 | Chuỗi |
Boolean | 5 | Đúng/Sai (Boolean) |
Player | 6 | Người chơi |
PlayerGrout | 7 | Nhóm người chơi |
BlockType | 8 | Loại khối |
ItemType | 9 | Loại vật phẩm |
Creature | 10 | Sinh vật |
ActorID | 11 | Loại nhân vật |
CreatureGroup | 12 | Nhóm sinh vật |
Timer | 13 | Hẹn giờ |
EffectType | 14 | Loại hiệu ứng đặc biệt |
SiteGroup | 15 | Nhóm vị trí |
AreaGroup | 16 | Nhóm khu vực |
NumberGroup | 17 | Nhóm giá trị |
StringGroup | 18 | Nhóm chuỗi |
BooleanGroup | 19 | Nhóm đúng/sai |
BlockTypeGroup | 20 | Nhóm loại khối |
ItemTypeGroup | 21 | Nhóm loại vật phẩm |
CreatureTypeGroup | 22 | Nhóm loại sinh vật |
TimerGroup | 23 | Nhóm hẹn giờ |
EffectTypeGroup | 24 | Nhóm loại hiệu ứng |
Tại sao bạn lại muốn lấy các biến trigger?
Nếu bạn đang theo dõi hướng dẫn này, rất có thể bạn muốn tạo game bằng cách kết hợp cả script và trigger – tận dụng ưu điểm của cả hai. Và thực tế, có những phương pháp giúp chúng ta làm được điều đó.
Việc lấy biến trigger cũng rất hữu ích vì bạn có thể sử dụng chúng như biến dùng chung giữa các script. Mini World không hỗ trợ phương thức require, vì vậy đây là một giải pháp thay thế tuyệt vời, cho phép bạn truy cập và sử dụng các biến có thể dùng chung trong mọi script.
Cách lấy biến toàn cục (global variables)
Để lấy các biến toàn cục thì rất dễ vì chúng ta có các phương thức để làm điều đó.
getGlobalVarByName: như bạn có thể đoán từ tên gọi, cho phép bạn lấy giá trị của một biến toàn cục.
setGlobalVarByName: cho phép bạn thay đổi giá trị của một biến toàn cục.
getGlobalVarByName
Hãy nói về phương thức này trước. Phương thức này nhận vào hai tham số:
- actionattr: số (Kiểu của biến)
- msg: chuỗi (Tên của biến)
Và trả về:
- Giá trị (Value – tùy thuộc vào loại biến)
Chúng ta sẽ làm một ví dụ sử dụng sau khi giải thích phương thức tiếp theo.
setGlobalVarByName
Phương thức này thay đổi giá trị của biến trigger mà chúng ta đã cung cấp. Nó nhận vào ba tham số:
- actionattr: số (Kiểu của biến)
- msg: chuỗi (Tên của biến)
- val: (phụ thuộc vào actionattr)
Và trả về:
Ví dụ sử dụng
Giả sử chúng ta muốn rằng khi một sinh vật bị đánh bại, một biến sẽ tăng lên 1. Chúng ta sẽ làm phần đó bằng trigger. Và khi biến đó lớn hơn hoặc bằng năm, chúng ta sẽ gửi một tin nhắn đến tất cả người chơi với nội dung “Bạn đã thắng!”, sau đó đặt lại biến đó về 0. Chúng ta sẽ làm phần đó bằng script.
Trigger
Script
Chúng ta bắt đầu bằng cách tạo một hàm, trong hàm này không cần tham số, rồi thêm Listener với sự kiện Actor.Die (Sinh vật chết) và hàm mà chúng ta vừa tạo. Mình sẽ không đi sâu chi tiết phần này vì nó đã được giải thích trong hướng dẫn về sự kiện Sử dụng sự kiện Mini World API.
Đoạn mã sẽ trông như thế này:
function CreatureDefeated() end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Sau đó, chúng ta thêm vào hàm phương thức getGlobalVarByName(actionattr, msg), như đã giải thích ở đầu hướng dẫn này.Tham số actionattr là kiểu của biến mà chúng ta muốn lấy. Có thể bạn sẽ thắc mắc làm sao biết được kiểu đó, và câu trả lời là bảng này chứa tất cả các kiểu biến có thể có cùng với giá trị của chúng.
Bảng
Tên | Giá trị | Mô tả |
Position | 1 | Vị trí |
AreaIns | 2 | Khu vực |
Number | 3 | Giá trị |
String | 4 | Chuỗi |
Boolean | 5 | Giá trị đúng/sai (Boolean) |
Player | 6 | Người chơi |
PlayerGrout | 7 | Tập hợp người chơi |
BlockType | 8 | Loại khối |
ItemType | 9 | Loại vật phẩm |
Creature | 10 | Sinh vật |
ActorID | 11 | Loại nhân vật |
CreatureGroup | 12 | Tập hợp sinh vật |
Timer | 13 | Bộ hẹn giờ |
EffectType | 14 | Loại hiệu ứng đặc biệt |
SiteGroup | 15 | Nhóm vị trí |
AreaGroup | 16 | Nhóm khu vực |
NumberGroup | 17 | Nhóm giá trị |
StringGroup | 18 | Nhóm chuỗi |
BooleanGroup | 19 | Nhóm giá trị đúng/sai |
BlockTypeGroup | 20 | Nhóm loại khối |
ItemTypeGroup | 21 | Nhóm loại vật phẩm |
CreatureTypeGroup | 22 | Nhóm loại sinh vật |
TimerGroup | 23 | Nhóm bộ hẹn giờ |
EffectTypeGroup | 24 | Nhóm loại hiệu ứng |
Biến của tôi là kiểu số, nên actionattr là 3. Nhưng sau khi xem bảng, có thể bạn sẽ thắc mắc, còn tham số msg thì sao? Nó chính là tên của biến mà chúng ta muốn lấy. Biến mà tôi tạo có tên là “MyTestVal”, nên msg tôi phải dùng là “MyTestVal”. Nhưng nếu tên biến của bạn khác, thì bạn phải dùng tên biến của bạn làm msg.
Các lỗi thường gặp:
function CreatureDefeated() local result = VarLib2:getGlobalVarByName(3,"MyTestVal") end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Lỗi này khá phổ biến, vì getGlobalVarByName trả về 2 giá trị (mã lỗi và giá trị) thay vì chỉ trả về giá trị. Nếu bạn cố gắng dùng biến kết quả result thì sẽ nhận được hoặc là 0 (ErrorCode.OK – không có lỗi), hoặc là 1001 (ErrorCode.FAILED – có lỗi xảy ra). Cách sửa là bạn phải khai báo thêm một biến nữa, ví dụ: local result, value = VarLib2:getGlobalVarByName(3, “MyTestVal”)
Mẹo: Nếu bạn không dùng một biến nào đó được trả về bởi bất kỳ phương thức nào, bạn có thể dùng dấu gạch dưới _ thay cho tên biến đó. Dấu “_” báo cho Lua biết biến đó sẽ không được sử dụng. Bạn có thể dùng “_” nhiều lần, ví dụ:
_, _, PlayerList = World:getAllPlayers(-1)
Còn nếu bạn không dùng “_” thì phải khai báo như thế này:
result, PlayerNum, PlayerList = World:getAllPlayers(-1)
Đoạn mã sẽ trông như thế này:
function CreatureDefeated() local result, value = VarLib2:getGlobalVarByName(3,"MyTestVal") end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Hoặc
function CreatureDefeated() local _, value = VarLib2:getGlobalVarByName(3,"MyTestVal") end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Cả hai cách đều giống nhau, nhưng cách trước tốt hơn nếu bạn muốn xử lý các lỗi có thể xảy ra.
Sau đó, chúng ta thêm một câu lệnh if kiểm tra xem value có lớn hơn hoặc bằng 5 không, nếu đúng thì gửi tin nhắn cho người chơi với nội dung “You won!” và đồng thời đặt lại biến của chúng ta về 0.
Phương thức setGlobalVarByName cũng có actionattr và msg, nên ta có thể sao chép từ getGlobalVarByName, nhưng tham số cuối cùng (val) là giá trị mà ta muốn biến đó có, trong trường hợp này là 0.
Đoạn mã sẽ trông như thế này:
function CreatureDefeated() local result, value = VarLib2:getGlobalVarByName(3,"MyTestVal") if value >= 5 then Chat:sendSystemMsg("You won!", 0) VarLib2:setGlobalVarByName(3,"MyTestVal",0) end end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Hoặc
function CreatureDefeated() local _, value = VarLib2:getGlobalVarByName(3,"MyTestVal") if value >= 5 then Chat:sendSystemMsg("You won!", 0) VarLib2:setGlobalVarByName(3,"MyTestVal",0) end end ScriptSupportEvent:registerEvent([=[Actor.Die]=], CreatureDefeated)
Cách lấy biến (biến riêng) của người chơi
Giống như với biến toàn cục, với biến riêng của người chơi cũng có các phương thức cho phép ta lấy giá trị được lưu trong đó. Các phương thức này đều theo cùng một cấu trúc như phương thức với biến toàn cục, chỉ có khác là ta phải chỉ định trước id của người chơi mà ta muốn lấy biến riêng của họ.
getPlayerVarByName
Lần này chúng ta sẽ nói về phương thức tương tự của getGlobalVarByName nhưng dành cho biến riêng. Phương thức này nhận ba tham số:
- objid: number (UID của người chơi mà ta sẽ lấy giá trị biến)
- actionattr: number (Kiểu của biến)
- msg: string (Tên của biến)
Và trả về:
- Giá trị (Tùy thuộc)
Chúng ta sẽ làm ví dụ sử dụng sau khi giải thích phương thức tiếp theo.
setPlayerVarByName
Phương thức này thay đổi giá trị của biến riêng mà ta đã chỉ định cho người chơi có UID mà ta cung cấp. Nó nhận bốn tham số:
- objid: number (UID của người chơi mà ta sẽ thay đổi giá trị biến)
- actionattr: number (Kiểu của biến)
- msg: string (Tên của biến)
- val (Tùy thuộc vào actionattr)
Và trả về:
Ví dụ sử dụng
Lần này chúng ta sẽ làm một điều khác, với các script ta sẽ làm tương tự, khi một người chơi đánh bại một con quái vật, ta sẽ cộng thêm 1 vào số quái vật mà người chơi đó đã đánh bại, và khi người chơi đánh bại được một số lượng quái vật, giả sử là 5, ta sẽ hiển thị thông báo “{PlayerNickname} đã lên cấp” trong đó {PlayerNickname} là chỗ dành sẵn cho tên của người chơi.
Trigger
Script
Bây giờ, để làm điều này, chúng ta sẽ cần hiểu rõ hơn một chút về các kiến thức cơ bản vì chúng ta sẽ sử dụng một thứ gọi là vòng lặp for dạng số (thêm thông tin tại trang Lua numeric for page) và vòng lặp while (thêm thông tin tại trang while loop).
Chúng ta bắt đầu giống như ví dụ trước, tạo một hàm không cần đối số, thêm Listener và thêm một vòng lặp while true bên trong hàm đó. Mã sẽ trông giống như sau:
function CreatureDefeated() while true do threadpool:wait(1) end end ScriptSupportEvent:registerEvent([=[Game.Start]=], CreatureDefeated)
Hàm threadpool:wait(seconds) rất quan trọng vì nếu chúng ta không thêm nó, trò chơi có thể bị treo. Sau đó, chúng ta thêm vào trong vòng lặp while hàm World:getAllPlayers(-1) — -1 có nghĩa là tất cả người chơi — và chúng ta thêm ba biến vì phương thức này trả về:
- Số lượng người chơi
- Một danh sách chứa tất cả ID của người chơi
Nhưng chúng ta chỉ cần danh sách ID. Sau đó, chúng ta thêm một vòng lặp for dạng số với i = 1, PlayerListLength và bên trong vòng lặp đó, chúng ta thêm phương thức getPlayerVarByName. Chúng ta đã biết cách lấy actionattr và msg, vì vậy sau khi hiện đoạn mã, chúng ta sẽ truyền objid.
Mã sẽ trông giống như sau:
function CreatureDefeated() while true do local _,_,PlayerList = World:getAllPlayers(-1) for i=1, #PlayerList do local result, value = VarLib2:setPlayerVarByName(PlayerList[i],3,"MyPrivateTestVar") Chat:sendSystemMsg(value) end Trigger:wait(1) end end ScriptSupportEvent:registerEvent([=[Game.Start]=], CreatureDefeated)
Sau khi xem đoạn mã, bạn có thể thắc mắc, tại sao chúng ta lại dùng PlayerList[i], câu trả lời nằm ở cách vòng lặp for hoạt động. Vòng lặp for hoạt động như sau: for initialvalue, endvalue, increment, trong đó:
- initialvalue là giá trị ban đầu mà chúng ta gán cho i
- endvalue là giá trị mà i sẽ tăng đến, và khi chạm đến giá trị này thì vòng lặp sẽ dừng
- increment là phần tùy chọn, nếu không khai báo thì mặc định sẽ là 1
Giá trị # tương đương với độ dài của một bảng (table), vì vậy chúng ta đang nói rằng giá trị bắt đầu của i là 1 và giá trị kết thúc là độ dài của PlayerList (số lượng người chơi hiện tại). Vậy nếu có 4 người chơi, thì độ dài của PlayerList sẽ là 4, do đó, vòng lặp sẽ chạy cho đến khi i bằng giá trị kết thúc (chạy 4 lần). Vậy [i] nghĩa là gọi phần tử ở vị trí i trong bảng.
Hãy xem một ví dụ nhỏ:
Người chơi | Vị trí trong bảng |
NotSoPr17 | 1 |
PvpGamesAreGreat | 2 |
MiniWorldPlayer | 3 |
ILoveMiniWorld | 4 |
Vậy ta biết có 4 người chơi nên độ dài playerlist sẽ là 4, sau đó ta thực hiện câu lệnh for.
for i=1, #playerlist (we know playerlist is 4 so it will be executed and i will increment by 1 until i is equal to 4) i -> 1 (NotSoPr17) i -> 2 (PvpGamesAreGreat) i -> 3 (MiniWorldPlayer) i -> 4 (ILoveMiniWorld)
Vậy nói đơn giản thì PlayerList[i] chính là ID của người chơi ở vị trí thứ i trong bảng.
Sau đó, chúng ta thêm một câu lệnh if để kiểm tra xem giá trị biến của người chơi có lớn hơn hoặc bằng 5 hay không. Đoạn mã lúc này sẽ trông giống như sau:
function CreatureDefeated() while true do local _,_,PlayerList = World:getAllPlayers(-1) for i=1, #PlayerList do local result, value = VarLib2:setPlayerVarByName(PlayerList[i],3,"MyPrivateTestVar") if value >= 5 then end end threadpool:wait(1) end end ScriptSupportEvent:registerEvent([=[Game.Start]=], CreatureDefeated)
Sau đó, khi một người chơi đánh bại hơn 5 quái vật hoặc người chơi khác, đoạn mã bên trong câu lệnh if sẽ được thực thi. Tiếp theo, ta thêm dòng local _, playerNick = Player:getNickname(PlayerList[i]) để lấy tên hiển thị (nickname) của người chơi hiện tại, và chúng ta tạo một hàm nhận vào nội dung (content) và danh sách người chơi (playerlist) để hiển thị một thông báo cho họ. Hàm này sẽ sử dụng vòng lặp for để làm việc đó.
Hàm sẽ trông giống như thế này:
function SendToAllPlayers(content,funcplayerlist) for i=1, #funcplayerlist do Chat:sendSystemMsg(content, funcplayerlist[i]) end end
Sau đó, ta thêm vào trong câu lệnh if phần kiểm tra xem giá trị có lớn hơn hoặc bằng 5 hay không, và sử dụng hàm setPlayerVarByName với objid là PlayerList[i].
Nếu bạn đang cảm thấy bối rối — điều này hoàn toàn bình thường khi học một cái gì đó mới — thì dưới đây là đoạn mã ví dụ để bạn tham khảo:
function SendToAllPlayers(content,funcplayerlist) for i=1, #funcplayerlist do Chat:sendSystemMsg(content, funcplayerlist[i]) end end function CreatureDefeated() while true do local _,_,PlayerList = World:getAllPlayers(-1) for i=1, #PlayerList do local result, value = VarLib2:getPlayerVarByName(PlayerList[i],3,"MyPrivateTestVar") if value >= 5 then local _,playerNick = Player:getNickname(PlayerList[i]) SendToAllPlayers(playerNick .. " has leveled up!", PlayerList) VarLib2:setPlayerVarByName(PlayerList[i],3,"MyPrivateTestVar",0) end end threadpool:wait(1) end end ScriptSupportEvent:registerEvent([=[Game.Start]=], CreatureDefeated)