cocos2d-Lua的运行流程与场景

本文详细解析了一个游戏项目的启动流程,从配置文件到主程序入口,再到各场景的加载与切换,最后聚焦于游戏核心逻辑所在的游戏视图组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

打开工程根目录下的配置文件config.json:
{
"init_cfg": {
"isLandscape": true,
"isWindowTop": false,
"name": "redDefense",
"width": 1920,
"height": 1080,
"entry": "src/main.lua",
"consolePort": 6050,
"uploadPort": 6060,
"debugPort": 10000,
"forwardConsolePort": 10089,
"forwardUploadPort": 10091
},
"simulator_screen_size": [
{
"title": "iPhone 3Gs (480x320)",
"width": 480,
"height": 320
},
{
"title": "iPhone 4 (960x640)",
"width": 960,
"height": 640
},
{
"title": "iPhone 5 (1136x640)",
"width": 1136,
"height": 640
},
{
"title": "iPad (1024x768)",
"width": 1024,
"height": 768
},
{
"title": "iPad Retina (2048x1536)",
"width": 2048,
"height": 1536
},
{
"title": "Android (800x480)",
"width": 800,
"height": 480
},
{
"title": "Android (854x480)",
"width": 854,
"height": 480
},
{
"title": "Android (1280x720)",
"width": 1280,
"height": 720
},
{
"title": "Android (1920x1080)",
"width": 1920,
"height": 1080
}
]
}
可以看到"entry": "src/main.lua",也就是说入口文件是main.lua,进而打开main.lua:
cc.FileUtils:getInstance():setPopupNotify(false)
cc.FileUtils:getInstance():addSearchPath("src/")
cc.FileUtils:getInstance():addSearchPath("res/")

require "config"
require "cocos.init"

local function main()
require("app.MyApp"):create():run()
end

local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
print(msg)
end
执行main函数,main函数里加载MyApp创建并运行,进而打开MyApp.lua:
local MyApp = class("MyApp", cc.load("mvc").AppBase)

function MyApp:onCreate()
math.randomseed(os.time())
end

return MyApp
这里就有点看头,MyApp仅仅是继承自AppBase,onCreate函数只是初始化了下随机数种子,也就意味着更多的操作在AppBase中,我们打开分析:
local AppBase = class("AppBase")

function AppBase:ctor(configs)
self.configs_ = {
viewsRoot = "app.views",
modelsRoot = "app.models",
defaultSceneName = "MainScene",
}

for k, v in pairs(configs or {}) do
self.configs_[k] = v
end

if type(self.configs_.viewsRoot) ~= "table" then
self.configs_.viewsRoot = {self.configs_.viewsRoot}
end
if type(self.configs_.modelsRoot) ~= "table" then
self.configs_.modelsRoot = {self.configs_.modelsRoot}
end

if DEBUG > 1 then
dump(self.configs_, "AppBase configs")
end

if CC_SHOW_FPS then
cc.Director:getInstance():setDisplayStats(true)
end

-- event
self:onCreate()
end

function AppBase:run(initSceneName)
initSceneName = initSceneName or self.configs_.defaultSceneName
self:enterScene(initSceneName)
end

function AppBase:enterScene(sceneName, transition, time, more)
local view = self:createView(sceneName)
view:showWithScene(transition, time, more)
return view
end

function AppBase:createView(name)
for _, root in ipairs(self.configs_.viewsRoot) do
local packageName = string.format("%s.%s", root, name)
local status, view = xpcall(function()
return require(packageName)
end, function(msg)
if not string.find(msg, string.format("'%s' not found:", packageName)) then
print("load view error: ", msg)
end
end)
local t = type(view)
if status and (t == "table" or t == "userdata") then
return view:create(self, name)
end
end
error(string.format("AppBase:createView() - not found view \"%s\" in search paths \"%s\"",
name, table.concat(self.configs_.viewsRoot, ",")), 0)
end

function AppBase:onCreate()
end

return AppBase
在前面的分析中知道main.lua是执行的是App的run函数,作为基类的AppBase,当然也要被调用run函数,因此直接看run函数:主要是创建并进入场景initSceneName,如果run的参数没有指定开始的场景则使用默认场景defaultSceneName,默认场景在构造函数的时候被初始化为MainScene,也就是说场景默认将从MainScene开始。

如果给run指定了场景名(字符串),那么项目启动后会直接进入该场景,这点有个好处是如果要调试设计某场景可以直接从这个场景进入,不必从其他场景进入了。也就是在main.lua中这么调用即可:
local function main()
require("app.MyApp"):create():run('PlayScene')
end
那么项目启动后会直接进入PlayScene场景,而不再是默认的MainScene场景。

我们现在不做改动,仍然接着默认流程分析,MainScene为主场景, 创建并显示一张背景图,创建显示一个“Play”按钮,按钮的点击事件是进入PlayScene场景。

local MainScene class("MainScene"cc.load("mvc").ViewBase)
function MainScene:onCreate()
-- add background image
display.newSprite("MainSceneBg.jpg")
:move(display.center)
:addTo(self)

-- add play button
local playButton = cc.MenuItemImage:create("PlayButton.png", "PlayButton.png")
:onClicked(function()
self:getApp():enterScene("PlayScene")
end)
cc.Menu:create(playButton)
:move(display.cx, display.cy - 200)
:addTo(self)
end

return MainScene


PlayScene场景:
local PlayScene = class("PlayScene", cc.load("mvc").ViewBase)

local GameView = import(".GameView")

function PlayScene:onCreate()
-- create game view and add it to stage
self.gameView_ = GameView:create()
:addEventListener(GameView.events.PLAYER_DEAD_EVENT, handler(self, self.onPlayerDead))
:start()
:addTo(self)
end

function PlayScene:onPlayerDead(event)
-- add game over text
local text = string.format("You killed %d bugs", self.gameView_:getKills())
cc.Label:createWithSystemFont(text, "Arial", 96)
:align(display.CENTER, display.center)
:addTo(self)

-- add exit button
local exitButton = cc.MenuItemImage:create("ExitButton.png", "ExitButton.png")
:onClicked(function()
self:getApp():enterScene("MainScene")
end)
cc.Menu:create(exitButton)
:move(display.cx, display.cy - 200)
:addTo(self)
end

return PlayScene
PlayScene场景创建游戏逻辑视图GameView并调用start函数开始游戏,绑定了一个游戏结束的事件,这个事件会在GameView中在触发游戏结束逻辑时发生,PlayScene由于绑定了该事件,则会在游戏结束时调用其绑定的回调事件,以显示战绩和分数,显示一个退出按钮,退出按钮事件为进入MainScene场景。

至此,场景的切换已经很清晰了,那么主要的游戏逻辑便是在GameView中了。

打开工程根目录下的配置文件config.json:
{
"init_cfg": {
"isLandscape": true,
"isWindowTop": false,
"name": "redDefense",
"width": 1920,
"height": 1080,
"entry": "src/main.lua",
"consolePort": 6050,
"uploadPort": 6060,
"debugPort": 10000,
"forwardConsolePort": 10089,
"forwardUploadPort": 10091
},
"simulator_screen_size": [
{
"title": "iPhone 3Gs (480x320)",
"width": 480,
"height": 320
},
{
"title": "iPhone 4 (960x640)",
"width": 960,
"height": 640
},
{
"title": "iPhone 5 (1136x640)",
"width": 1136,
"height": 640
},
{
"title": "iPad (1024x768)",
"width": 1024,
"height": 768
},
{
"title": "iPad Retina (2048x1536)",
"width": 2048,
"height": 1536
},
{
"title": "Android (800x480)",
"width": 800,
"height": 480
},
{
"title": "Android (854x480)",
"width": 854,
"height": 480
},
{
"title": "Android (1280x720)",
"width": 1280,
"height": 720
},
{
"title": "Android (1920x1080)",
"width": 1920,
"height": 1080
}
]
}
可以看到"entry": "src/main.lua",也就是说入口文件是main.lua,进而打开main.lua:
cc.FileUtils:getInstance():setPopupNotify(false)
cc.FileUtils:getInstance():addSearchPath("src/")
cc.FileUtils:getInstance():addSearchPath("res/")

require "config"
require "cocos.init"

local function main()
require("app.MyApp"):create():run()
end

local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
print(msg)
end
执行main函数,main函数里加载MyApp创建并运行,进而打开MyApp.lua:
local MyApp = class("MyApp", cc.load("mvc").AppBase)

function MyApp:onCreate()
math.randomseed(os.time())
end

return MyApp
这里就有点看头,MyApp仅仅是继承自AppBase,onCreate函数只是初始化了下随机数种子,也就意味着更多的操作在AppBase中,我们打开分析:
local AppBase = class("AppBase")

function AppBase:ctor(configs)
self.configs_ = {
viewsRoot = "app.views",
modelsRoot = "app.models",
defaultSceneName = "MainScene",
}

for k, v in pairs(configs or {}) do
self.configs_[k] = v
end

if type(self.configs_.viewsRoot) ~= "table" then
self.configs_.viewsRoot = {self.configs_.viewsRoot}
end
if type(self.configs_.modelsRoot) ~= "table" then
self.configs_.modelsRoot = {self.configs_.modelsRoot}
end

if DEBUG > 1 then
dump(self.configs_, "AppBase configs")
end

if CC_SHOW_FPS then
cc.Director:getInstance():setDisplayStats(true)
end

-- event
self:onCreate()
end

function AppBase:run(initSceneName)
initSceneName = initSceneName or self.configs_.defaultSceneName
self:enterScene(initSceneName)
end

function AppBase:enterScene(sceneName, transition, time, more)
local view = self:createView(sceneName)
view:showWithScene(transition, time, more)
return view
end

function AppBase:createView(name)
for _, root in ipairs(self.configs_.viewsRoot) do
local packageName = string.format("%s.%s", root, name)
local status, view = xpcall(function()
return require(packageName)
end, function(msg)
if not string.find(msg, string.format("'%s' not found:", packageName)) then
print("load view error: ", msg)
end
end)
local t = type(view)
if status and (t == "table" or t == "userdata") then
return view:create(self, name)
end
end
error(string.format("AppBase:createView() - not found view \"%s\" in search paths \"%s\"",
name, table.concat(self.configs_.viewsRoot, ",")), 0)
end

function AppBase:onCreate()
end

return AppBase
在前面的分析中知道main.lua是执行的是App的run函数,作为基类的AppBase,当然也要被调用run函数,因此直接看run函数:主要是创建并进入场景initSceneName,如果run的参数没有指定开始的场景则使用默认场景defaultSceneName,默认场景在构造函数的时候被初始化为MainScene,也就是说场景默认将从MainScene开始。

如果给run指定了场景名(字符串),那么项目启动后会直接进入该场景,这点有个好处是如果要调试设计某场景可以直接从这个场景进入,不必从其他场景进入了。也就是在main.lua中这么调用即可:
local function main()
require("app.MyApp"):create():run('PlayScene')
end
那么项目启动后会直接进入PlayScene场景,而不再是默认的MainScene场景。

我们现在不做改动,仍然接着默认流程分析,MainScene为主场景, 创建并显示一张背景图,创建显示一个“Play”按钮,按钮的点击事件是进入PlayScene场景。

local MainScene class("MainScene"cc.load("mvc").ViewBase)
function MainScene:onCreate()
-- add background image
display.newSprite("MainSceneBg.jpg")
:move(display.center)
:addTo(self)

-- add play button
local playButton = cc.MenuItemImage:create("PlayButton.png", "PlayButton.png")
:onClicked(function()
self:getApp():enterScene("PlayScene")
end)
cc.Menu:create(playButton)
:move(display.cx, display.cy - 200)
:addTo(self)
end

return MainScene


PlayScene场景:
local PlayScene = class("PlayScene", cc.load("mvc").ViewBase)

local GameView = import(".GameView")

function PlayScene:onCreate()
-- create game view and add it to stage
self.gameView_ = GameView:create()
:addEventListener(GameView.events.PLAYER_DEAD_EVENT, handler(self, self.onPlayerDead))
:start()
:addTo(self)
end

function PlayScene:onPlayerDead(event)
-- add game over text
local text = string.format("You killed %d bugs", self.gameView_:getKills())
cc.Label:createWithSystemFont(text, "Arial", 96)
:align(display.CENTER, display.center)
:addTo(self)

-- add exit button
local exitButton = cc.MenuItemImage:create("ExitButton.png", "ExitButton.png")
:onClicked(function()
self:getApp():enterScene("MainScene")
end)
cc.Menu:create(exitButton)
:move(display.cx, display.cy - 200)
:addTo(self)
end

return PlayScene
PlayScene场景创建游戏逻辑视图GameView并调用start函数开始游戏,绑定了一个游戏结束的事件,这个事件会在GameView中在触发游戏结束逻辑时发生,PlayScene由于绑定了该事件,则会在游戏结束时调用其绑定的回调事件,以显示战绩和分数,显示一个退出按钮,退出按钮事件为进入MainScene场景。

至此,场景的切换已经很清晰了,那么主要的游戏逻辑便是在GameView中了。
<think>好的,我现在需要帮用户解决他们在使用xmake时遇到的“xmake.lua not found”的问题。用户之前已经询问过如何验证xmake是否安装成功,现在他们遇到了具体的错误提示,并希望了解如何处理这个提示以及输入选项的意义。 首先,我需要理解用户的问题。错误信息指出xmake.lua文件未找到,xmake尝试生成它,但需要用户确认。用户输入了“n”,也就是否,然后可能想知道接下来该怎么做或者这个提示的含义。用户可能是在尝试编译项目时遇到了这个问题,尤其是在没有xmake.lua文件的情况下。 接下来,我需要分析错误的原因。xmake.lua是xmake的配置文件,类似于Makefile。当用户运行xmake命令时,xmake会在当前目录查找这个文件。如果不存在,xmake会询问是否自动生成一个默认的配置文件。用户输入“n”拒绝了生成,因此xmake停止操作,导致编译失败。 用户可能的疑问包括:为什么需要这个文件?输入y/n/d分别代表什么?如果拒绝生成,该如何继续?或者如何手动创建这个文件? 接下来,我需要考虑用户的使用场景。用户可能是一个刚开始使用xmake的开发者,可能不熟悉项目配置文件的创建流程。他们可能是在尝试构建一个项目,但尚未配置xmake.lua,或者在错误目录下运行了命令。需要指导他们正确创建或生成配置文件,或者检查当前目录是否正确。 然后,我需要确定回答的结构。首先解释错误信息,说明xmake.lua的作用,然后解释各选项(y/n/d)的含义,接着给出拒绝生成后的解决方案,比如手动创建xmake.lua或确认目录是否正确。最后,提供预防措施,比如使用模板项目或详细配置方法。 需要注意用户的潜在需求。用户可能不想自动生成配置文件,而是希望手动创建,或者他们可能误在错误目录运行命令。需要覆盖这些可能性,并提供相应的解决方案。 另外,要确保回答清晰,步骤明确,避免技术术语过多,适合刚接触xmake的用户理解。同时,要参考之前的对话历史,用户之前已经安装了xmake并验证过,所以问题可能出在项目配置上,而不是安装问题。 最后,检查是否有遗漏点,比如权限问题或路径问题,但根据错误信息,主要问题在于缺少xmake.lua,因此重点应放在该文件的生成和配置上。</think>--- ### 问题分析 当运行 `xmake` 命令时出现 `note: xmake.lua not found` 提示,表明 **当前目录没有检测到 xmake 的工程配置文件**(类似 CMakeLists.txt)。此时 xmake 询问是否自动生成默认配置文件,你输入了 `n`(拒绝生成)。以下是详细解释和解决方案: --- ### 各选项含义 | 选项 | 全称 | 行为 | |------|------------|----------------------------------------------------------------------| | `y` | yes | 自动生成默认的 `xmake.lua` 文件 | | `n` | no | 拒绝生成,终止当前操作 | | `d` | disable | 拒绝生成,并临时禁用本次会话中的自动检测(后续操作不再弹出此提示) | --- ### 输入 `n` 后的解决方案 #### 情况 1:你希望手动创建项目 1. **手动编写 `xmake.lua`** 在项目根目录新建文件 `xmake.lua`,按需添加配置(例如编译 C 语言项目): ```lua target("myapp") set_kind("binary") add_files("src/*.c") ``` 2. 重新运行 `xmake` 命令。 #### 情况 2:误操作或目录错误 1. **确认当前目录是否正确** 使用 `cd` 切换到已有 `xmake.lua` 的项目目录。 2. **重新生成配置文件(可选)** 若需重新生成,可运行: ```bash xmake create -l c # 生成 C 语言项目模板 ``` 或直接输入: ```bash xmake # 再次触发提示时输入 `y` ``` --- ### 预防措施 | 场景 | 建议操作 | |--------------------------|--------------------------------------------------------------------------| | 快速创建新项目 | 使用 `xmake create -l c/c++` 生成完整项目模板(含预配置的 `xmake.lua`) | | 已有代码需集成 xmake | 参考 [官方文档](https://xmake.io/#/getting_started) 手动编写配置文件 | | 临时禁用自动检测 | 输入 `d` 或运行命令时附加 `--confirm=d`(例如 `xmake --confirm=d`) | --- ### 操作流程图 ```plaintext 运行 xmake → 无 xmake.lua → 提示生成? ├─ 输入 y → 自动生成 → 继续编译 ├─ 输入 n → 终止操作(需手动处理) └─ 输入 d → 终止操作,并禁用本次会话的自动检测 ``` 通过以上步骤,可灵活控制 xmake 的工程配置行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值