├── .gitignore ├── README.md ├── encrypt_res.py ├── framework_build.sh ├── game_build.sh ├── init_build.sh ├── loader_build.sh ├── make_update_files.py ├── res └── images │ ├── login.png │ ├── progress.png │ └── progress_bg.png ├── src ├── app │ ├── MyApp.lua │ ├── const.lua │ ├── init.lua │ └── scenes │ │ └── LoadScene.lua ├── config.lua └── main.lua └── xcode_fresh.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.class 3 | .data/ 4 | update_build/ 5 | build/ 6 | .DS_Store 7 | log/ 8 | *.log 9 | *.txt 10 | res/resindex.txt 11 | *.cache 12 | *.o 13 | *.o.d 14 | gdbserver 15 | gdb.setup 16 | *.apk 17 | config/ 18 | *.sdf 19 | */Debug.win32/ 20 | *.tlog 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quick-cocos2d-x 热更新项目 jw_loader 的示例工程 jw_loader_samples 2 | 3 | ## 使用步骤: 4 | 5 | 1. 安装社区版 quick-cocos2d-x 3.6 6 | 2. 下载关联的热更工程 https://github.com/uhoohei/jw_loader.git 7 | 3. 使用quick创建你的空工程,并删除空工程下的 src res 目录 8 | 4. 将samples中的 res src 以及根目录下的所有sh脚本和py脚本拷贝到空工程中 9 | 5. 搜索全工程目录,替换TODO_SET_PWD 与 YOUR_SIGN 为你的资源和代码加密密码和加密签名 10 | 6. 运行本示例的根目录下的 loader_build.sh 来编译 loader32.zip与loader64.zip 文件,编译成功的话此文件直接进入 res 目录。 11 | 7. 运行本示例的根目录下的 framework_build.sh 来编译 quick 的 framework,成功的话可以在res目录下看到 framework32.zip 与 framework64.zip 12 | 8. 运行项目根目录下的 ./game_build.sh [w|t] 脚本来生成客户端初始化init32.zip与init64.zip及game32.zip与game64.zip,并生成检测热更文件所需要的索引文件,同时在目录update_build 下生成所有热更所需要的MD5形式的文件 13 | 9. 查看下面的“修改工程入口以支持加密加载”一节以支持加密的资源和代码的加载 14 | 10. enjoy it! 15 | 16 | 17 | ## 热更的关键参数说明 18 | 19 | ### 游戏ID,不同的游戏以此作为区别 20 | 文件:src/config.lua 21 | 变量名:GAME_ID 22 | 它由客户端定义并使用,同时也被 make_update_files.py 所读取 23 | 24 | ### 主版本号 25 | 文件:src/config.lua 26 | 变量名:VERSION_HOST 27 | 主版本号在脚本里面有定义,在安卓的清单文件中也有定义,在IOS的PLIST中也有定义,脚本里面的只是基本值,它同时也需要被 make_update_files.py 所读取,版本号的第一位数字是主版本,热更时会判断此值,***不推荐跨主版本***进行热更,一般用大版本号来表示项目引擎本身的变更,这些变更可能在 28 | 脚本层不是兼容的 29 | 30 | ### 分支ID 31 | 文件:src/config.lua 32 | 变量名:BRANCH_ID 33 | 分支ID的定义和作用是为了区别不同的第三方平台或SDK,有时候为了发布某些渠道,可能要接入他们的SDK,这样的 34 | 话他们的代码与主分支可能稍有不同,此时为了区别更新,引入了分支ID的概念 35 | 36 | ### 脚本版本ID 37 | 文件:src/app/const.lua 38 | 变量名:SCRIPT_VERSION_ID 39 | 脚本版本ID是唯一用来比较判断是否有新版本的变量,当线上版本大于本地版本时,才有后续的热更流程, 40 | 每一次发布热更版本都必须改变此值并用热更的打包脚本进行编译 41 | 42 | ### UPDATE_PATH 43 | 更新的工作路径 44 | 45 | ### GAME_ENTRANCE 46 | 游戏的真正入口,热更完成后会加载此入口文件并new出来进行真正的进入游戏的过程 47 | 48 | ### 环境ID 49 | 此处定义了2个 50 | * w: 正式环境 51 | * t: 测试环境 52 | 执行 game_build.sh 脚本生成索引时,一定要注意此项 53 | 54 | ## 热更的打包流程 55 | 一般framework_build.sh与loader_build.sh在没有新的变更的时候不需要每次都编译。 56 | 先执行工程根目录下的 game_build.sh [w|t] 57 | 此脚本需要输入当前是为哪个环境所编译资源,具体可看命令行提示 58 | 59 | 执行完后成功后, 60 | 将所生成的 update_build 目录下的指定目录的文件传给服务器管理人员,由他们更新发布 61 | 62 | ## Native 工程说明 63 | 本示例中只有脚本部分的内容 64 | IOS与ANDROID都需要一些NATIVE的代码来实现获得NATIVE的环境ID,NATIVE的原生版本号等。 65 | 这部分网上很多,每个人的实现都有些不同,这里不提供,这部分的配置请参考 main.lua 中的注释进行说明 66 | 67 | ## 修改工程入口以支持加密加载 68 | 修改工程文件 Classes/AppDelegate.cpp,我的3.6.3是从104行开始,以支持资源和代码的加密 69 | **请修改下列SET_YOUR_PWD为你的真实项目密码** 70 | 当然你也可以不修改,那样你需要去掉所有脚本中的与加密相关的配置选项 71 | ```c++ 72 | FileUtils::getInstance()->setResourceEncryptKeyAndSign("SET_YOUR_PWD", "YOUR_SIGN"); 73 | #if 1 74 | // use luajit bytecode package 75 | stack->setXXTEAKeyAndSign("SET_YOUR_PWD", "YOUR_SIGN"); 76 | 77 | #ifdef CC_TARGET_OS_IPHONE 78 | if (sizeof(long) == 4) { 79 | stack->loadChunksFromZIP("res/init32.zip"); 80 | } else { 81 | stack->loadChunksFromZIP("res/init64.zip"); 82 | } 83 | #else 84 | // android, mac, win32, etc 85 | stack->loadChunksFromZIP("res/init32.zip"); 86 | #endif 87 | stack->executeString("require 'main'"); 88 | #else // #if 0 89 | // use discrete files 90 | engine->executeScriptFile("src/main.lua"); 91 | #endif 92 | ``` 93 | 94 | -------------------------------------------------------------------------------- /encrypt_res.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | # coding:utf-8 3 | 4 | # 资源加密脚本 5 | 6 | 7 | import sys 8 | import os 9 | import subprocess 10 | import shutil 11 | from copy import deepcopy 12 | 13 | 14 | BASE_PATH = os.path.dirname(os.path.realpath(__file__)) + os.sep 15 | res_path = BASE_PATH + "res" # 行尾无/号 16 | build_path = BASE_PATH + "build/res" # 行尾无/号 17 | # TODO: 在这里修改你的项目的加密密码,不得超过16位,记得与game_build.sh脚本中的密码一致 18 | sign_key = "TODO_SET_PWD" # 不得超过16位 19 | ignore_list = [ # 一行一个,设定不要加密的资源文件名称 20 | "images/login.png", 21 | ] 22 | 23 | 24 | def file_extension(path): # 文件扩展名 25 | return os.path.splitext(path)[1] 26 | 27 | 28 | def remove_file(file_path): # 删除文件 29 | if os.path.isfile(file_path): 30 | os.remove(file_path) 31 | 32 | 33 | def remove_path(path): # 删除目录 34 | if path == "/" or path == ".": 35 | print("Can't do this action: remove whole ", path) 36 | return 37 | if os.path.isdir(path): 38 | shutil.rmtree(path) 39 | 40 | 41 | def make_path(path): 42 | if os.path.isdir(path): 43 | return 44 | os.makedirs(path) 45 | 46 | 47 | def copy_file(source, dest): # 拷贝文件 48 | shutil.copy(source, dest) 49 | 50 | 51 | def copy_path(source_path, dest_path): # 拷贝目录 52 | shutil.copytree(source_path, dest_path) 53 | 54 | 55 | def list_path_by_handler(root_dir, callback_handler): # 带回调函数的遍历文件夹 56 | for lists in os.listdir(root_dir): 57 | path = os.path.join(root_dir, lists) 58 | # print path 59 | if os.path.isdir(path): 60 | list_path_by_handler(path, callback_handler) 61 | else: 62 | callback_handler(path) 63 | 64 | 65 | def copy_no_encrypt_file_handler(file_path): 66 | for item in ignore_list: # 跳过不加密的资源 67 | if item in file_path: 68 | dest_file = build_path + file_path.replace(res_path, "") 69 | print("copy no encrypt file: ", file_path, dest_file) 70 | copy_file(file_path, dest_file) 71 | return 72 | 73 | 74 | remove_path(build_path) 75 | cmd = "$QUICK_V3_ROOT/quick/bin/encrypt_res.sh -i %s -o %s -es YOUR_SIGN -ek %s" % \ 76 | (res_path, build_path, sign_key) 77 | subprocess.call(cmd, shell=True) 78 | list_path_by_handler(res_path, copy_no_encrypt_file_handler) 79 | -------------------------------------------------------------------------------- /framework_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 引擎编译脚本,负责编译 cocos 和 framework 目录到 res 目录中去 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | DEST_DIR=$DIR/res 7 | 8 | #框架的源码包 9 | COCOS_SOURCE=$QUICK_V3_ROOT/quick/cocos 10 | FRAMEWORK_SOURCE=$QUICK_V3_ROOT/quick/framework 11 | TMP_PATH=$QUICK_V3_ROOT/quick/tmp 12 | FRAMEWORK_TARGET=framework 13 | 14 | file32=$FRAMEWORK_TARGET"32.zip" 15 | file64=$FRAMEWORK_TARGET"64.zip" 16 | 17 | rm -f $DEST_DIR/$file32 18 | rm -f $DEST_DIR/$file64 19 | rm -rf $TMP_PATH 20 | mkdir $TMP_PATH 21 | cp -rf $COCOS_SOURCE $TMP_PATH 22 | cp -rf $FRAMEWORK_SOURCE $TMP_PATH 23 | 24 | COMPILE_BIN=$QUICK_V3_ROOT/quick/bin/compile_scripts.sh 25 | 26 | ENCRYPT_COMMAND="" 27 | if [ $1 ]; then 28 | ENCRYPT_COMMAND=" -e xxtea_zip -ek $1 -es YOUR_SIGN " 29 | fi 30 | 31 | # 编译framework脚本文件 32 | $COMPILE_BIN -b 32 -i $TMP_PATH -o $DEST_DIR/$file32 $ENCRYPT_COMMAND 33 | $COMPILE_BIN -b 64 -i $TMP_PATH -o $DEST_DIR/$file64 $ENCRYPT_COMMAND 34 | rm -rf $TMP_PATH 35 | -------------------------------------------------------------------------------- /game_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | SCRIPTS_DIR=$DIR/src 5 | DEST_DIR=$DIR/.data 6 | TARGET_FILE=game 7 | 8 | if [ ! $1 ]; then 9 | echo "用法:./game_build.sh w|t(正式|测试环境标识)" 10 | exit; 11 | fi 12 | 13 | COMPILE_BIN=$QUICK_V3_ROOT/quick/bin/compile_scripts.sh 14 | 15 | mkdir $DEST_DIR 16 | 17 | # 编译游戏脚本文件 18 | file32=$TARGET_FILE"32.zip" 19 | file64=$TARGET_FILE"64.zip" 20 | rm -f $DEST_DIR/$file32 21 | rm -f $DEST_DIR/$file64 22 | 23 | # TODO: 在这里修改你的项目的加密密码,不得超过16位,记得也修改encrypt_res.py脚本中的资源的密码 24 | PASSWORD=TODO_SET_PWD 25 | ENCRYPT_COMMAND=" -e xxtea_zip -ek $PASSWORD -es YOUR_SIGN " 26 | 27 | $COMPILE_BIN -b 32 -i $SCRIPTS_DIR -o $DEST_DIR/$file32 $ENCRYPT_COMMAND 28 | $COMPILE_BIN -b 64 -i $SCRIPTS_DIR -o $DEST_DIR/$file64 $ENCRYPT_COMMAND 29 | 30 | # 编译入口文件 31 | source $DIR/init_build.sh $PASSWORD 32 | 33 | build_path=$DIR"/build/" 34 | rm -rf $build_path 35 | mkdir -p $build_path 36 | 37 | python $DIR/encrypt_res.py 38 | 39 | cp -rf "$DIR"/.data/*32.zip $build_path/res/ 40 | cp -rf "$DIR"/.data/*64.zip $build_path/res/ 41 | 42 | python $DIR/make_update_files.py $1 43 | -------------------------------------------------------------------------------- /init_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | SCRIPTS_DIR=$DIR/.data/src 5 | DEST_DIR=$DIR/.data 6 | TARGET_FILE=init 7 | 8 | COMPILE_BIN=$QUICK_V3_ROOT/quick/bin/compile_scripts.sh 9 | 10 | mkdir $DEST_DIR 11 | mkdir $SCRIPTS_DIR 12 | 13 | # 编译游戏脚本文件 14 | file32=$TARGET_FILE"32.zip" 15 | file64=$TARGET_FILE"64.zip" 16 | 17 | rm $SCRIPTS_DIR/*.lua 18 | rm -f $DEST_DIR/$file32 19 | rm -f $DEST_DIR/$file64 20 | 21 | cp -f $DIR/src/*.lua $SCRIPTS_DIR/ 22 | 23 | ENCRYPT_COMMAND="" 24 | if [ $1 ]; then 25 | ENCRYPT_COMMAND=" -e xxtea_zip -ek $1 -es YOUR_SIGN " 26 | fi 27 | 28 | $COMPILE_BIN -b 32 -i $SCRIPTS_DIR -o $DEST_DIR/$file32 $ENCRYPT_COMMAND 29 | $COMPILE_BIN -b 64 -i $SCRIPTS_DIR -o $DEST_DIR/$file64 $ENCRYPT_COMMAND 30 | -------------------------------------------------------------------------------- /loader_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 这是个更新 loader.zip 的快捷工具,请注意自己的路径 4 | 5 | MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | MY_DEST_DIR=$MY_DIR/res 7 | 8 | # TODO: 修改下面的目录为你的jw_loader项目的实际目录 9 | source $QUICK_V3_ROOT/../jw_loader/build_luajit.sh $1 10 | 11 | echo "mv $QUICK_V3_ROOT/../jw_loader/*.zip $MY_DEST_DIR/" 12 | mv $QUICK_V3_ROOT/../jw_loader/*.zip $MY_DEST_DIR/ 13 | -------------------------------------------------------------------------------- /make_update_files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | #coding:utf-8 3 | import sys, os, json, time, hashlib 4 | import subprocess 5 | import shutil 6 | from copy import deepcopy 7 | 8 | BASE_PATH = os.path.dirname(os.path.realpath(__file__)) + os.sep 9 | 10 | # 文件扩展名 11 | def file_extension(path): 12 | return os.path.splitext(path)[1] 13 | 14 | def md5(fname): 15 | hash_md5 = hashlib.md5() 16 | with open(fname, "rb") as f: 17 | for chunk in iter(lambda: f.read(4096), b""): 18 | hash_md5.update(chunk) 19 | return hash_md5.hexdigest() 20 | 21 | # 计算文件MD5值 22 | def calc_md5(path): 23 | return md5(path) 24 | 25 | # 删除文件 26 | def removeFile(filePath): 27 | assert(filePath) 28 | if os.path.isfile(filePath): 29 | os.remove(filePath) 30 | 31 | # 删除目录 32 | def removePath(path): 33 | assert(path) 34 | if path == "/" or path == ".": 35 | print "Can't do this action: remove whole ", path 36 | return 37 | if os.path.isdir(path): 38 | shutil.rmtree(path) 39 | 40 | # 拷贝文件 41 | def copyFile(source, dest): 42 | assert(source and dest) 43 | shutil.copy(source, dest) 44 | 45 | # 拷贝目录 46 | def copyPath(sourcePath, destPath): 47 | assert(sourcePath and destPath) 48 | shutil.copytree(sourcePath, destPath) 49 | 50 | # 遍历文件夹 51 | def listPathByHandler(rootDir, handler): 52 | for lists in os.listdir(rootDir): 53 | path = os.path.join(rootDir, lists) 54 | # print path 55 | if os.path.isdir(path): 56 | listPathByHandler(path, handler) 57 | else: 58 | handler(path) 59 | 60 | # 分析lua的配置文件,取出其中的 VERSION_NAME GAME_ID BRANCH_ID 这几项 61 | # 注意不能分析复杂的配置项,这里只是为了读出数字和字符串的配置 62 | def readLuaConfig(filename, key): 63 | file = open(filename) 64 | while 1: 65 | line = file.readline() 66 | if not line: 67 | break 68 | else: 69 | pos = line.find("--") 70 | line = line.replace(line[pos:], "") # 清除注释 71 | line = line.replace("\t", "") # 清除TAB 72 | line = line.replace(" ", "") # 清除空格 73 | line = line.replace("\"", "") # 清除引号 74 | line = line.replace("'", "") # 清除引号 75 | if len(line) < 1: 76 | continue 77 | obj = line.split("=") 78 | if not obj: 79 | continue 80 | if obj[0] == key: # 找到值了,返回 81 | return obj[1] 82 | 83 | return None 84 | 85 | # 生成文件指纹树 86 | def calcFileFingerAndCopyHandler(filePath): 87 | for x in ignoreFiles: 88 | if x in filePath: 89 | return 90 | 91 | md5 = calc_md5(filePath) 92 | key = filePath.replace(TMP_PATH, "") 93 | assetsTree[key] = [os.path.getsize(filePath), md5] 94 | copyFile(filePath, RES_DEST_PATH + md5) 95 | 96 | 97 | # 从 lua 文件中找到对应的配置项 98 | VERSION_NAME = readLuaConfig(BASE_PATH + "src/config.lua", "VERSION_HOST") # 版本号 99 | GAME_ID = readLuaConfig(BASE_PATH + "src/config.lua", "GAME_ID") # 游戏ID 100 | BRANCH_ID = readLuaConfig(BASE_PATH + "src/config.lua", "BRANCH_ID") # 分支ID 101 | SCRIPT_VERSION = int(readLuaConfig(BASE_PATH + "src/app/const.lua", "SCRIPT_VERSION_ID")) # 脚本版本号 102 | MAIN_VERSION = VERSION_NAME.split('.')[0] # 主版本号 103 | 104 | 105 | # TODO: 修改这里的链接为你的服务端的下载链接 106 | URLS = { 107 | "w": "https://down.samples.com/update", 108 | "t": "https://test.down.samples.com/update", 109 | } 110 | 111 | if not sys.argv or len(sys.argv) < 2 or sys.argv[1] not in URLS.keys(): 112 | print '用法:', sys.argv[0], URLS.keys() 113 | sys.exit() 114 | 115 | ENV_ID = sys.argv[1] 116 | 117 | print "生成热更文件列表" 118 | print "注意:", " 输入的环境ID为:", ENV_ID, " 热更新的根URL为:", URLS.get(ENV_ID) 119 | print 120 | print "版本号: ", VERSION_NAME, " 游戏ID: ", GAME_ID, " 分支ID: ", BRANCH_ID, " 脚本版本ID: ", str(SCRIPT_VERSION) 121 | time.sleep(2) 122 | 123 | ignoreFiles = (".DS_Store", "Thumb.db") # 要忽略的文件 124 | RES_PATH = BASE_PATH + "build/res" # 要扫描的目录 125 | versionFile = "version.txt" 126 | indexFile = "resindex.txt" 127 | BASE_URL = URLS[ENV_ID] 128 | subPath = GAME_ID + '/v' + MAIN_VERSION + '_' + BRANCH_ID + "/" 129 | url = BASE_URL + '/' + subPath 130 | BUILD_PATH = BASE_PATH + "update_build/" + subPath # 编译路径 131 | RES_DEST_PATH = BUILD_PATH + "resources/" 132 | TMP_PATH = BUILD_PATH + "tmp/" 133 | 134 | versionTree = { 135 | "gameId" : int(GAME_ID), 136 | "branchId" : BRANCH_ID, 137 | "versionURL" : url + versionFile, 138 | "indexURL" : url + indexFile, 139 | "downloadURL" : url + 'resources/', 140 | "mainVersion" : MAIN_VERSION, 141 | "scriptVersion" : SCRIPT_VERSION, 142 | "envId" : ENV_ID, 143 | } 144 | assetsTree = {} 145 | rootTree = deepcopy(versionTree) 146 | rootTree['assets'] = assetsTree 147 | 148 | 149 | removePath(BUILD_PATH) # 先清空编译目录 150 | os.makedirs(RES_DEST_PATH) 151 | copyPath(RES_PATH, TMP_PATH) # 拷贝资源目录 152 | removeFile(TMP_PATH + indexFile) # 删除不需要的索引文件 153 | listPathByHandler(TMP_PATH, calcFileFingerAndCopyHandler) # 生成所有文件的指纹树 154 | 155 | 156 | # 写入文件 157 | f2 = open(BUILD_PATH + indexFile, 'w') 158 | f2.write(json.dumps(rootTree)) 159 | f2.close() 160 | 161 | versionTree['indexSign'] = calc_md5(BUILD_PATH + indexFile) # 更新project文件的md5,以方便下载后验证 162 | f1 = open(BUILD_PATH + versionFile, 'w') 163 | f1.write(json.dumps(versionTree)) 164 | f1.close() 165 | 166 | copyFile(BUILD_PATH + indexFile, RES_PATH) # 为本项目更新索引文件 167 | removePath(TMP_PATH) 168 | -------------------------------------------------------------------------------- /res/images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhoohei/jw_loader_samples/f6d82fded4ac869b55da013d03fe2ca6c4108fbc/res/images/login.png -------------------------------------------------------------------------------- /res/images/progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhoohei/jw_loader_samples/f6d82fded4ac869b55da013d03fe2ca6c4108fbc/res/images/progress.png -------------------------------------------------------------------------------- /res/images/progress_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhoohei/jw_loader_samples/f6d82fded4ac869b55da013d03fe2ca6c4108fbc/res/images/progress_bg.png -------------------------------------------------------------------------------- /src/app/MyApp.lua: -------------------------------------------------------------------------------- 1 | require("app.init") 2 | 3 | local MyApp = class("MyApp", cc.mvc.AppBase) 4 | 5 | function MyApp:ctor() 6 | MyApp.super.ctor(self) 7 | math.newrandomseed() 8 | cc.Director:getInstance():setAnimationInterval(1 / (CONFIG_FPS_NUMBERS or 30)) 9 | self:init_() 10 | end 11 | 12 | function MyApp:init_() 13 | end 14 | 15 | function MyApp:run() 16 | if CC_DISABLE_GLOBAL then 17 | -- jw.utils.dennyGlobalVariable() 18 | end 19 | 20 | self:enterScene("LoadScene") 21 | end 22 | 23 | return MyApp 24 | -------------------------------------------------------------------------------- /src/app/const.lua: -------------------------------------------------------------------------------- 1 | 2 | SCRIPT_VERSION_ID = 6 -- 脚本版本ID 3 | 4 | HEART_BEAT_SECONDS = 5 -- 心跳时间间隔 5 | 6 | CHECK_NETWORK = true -- 是否开启强制网络检测 7 | if DEBUG < 1 then -- 正式环境必须要开断网检测 8 | CHECK_NETWORK = true 9 | end 10 | 11 | 12 | --[[ 13 | .... 14 | ... 15 | 继续写一些全局配置 16 | ]] 17 | -------------------------------------------------------------------------------- /src/app/init.lua: -------------------------------------------------------------------------------- 1 | function __G__TRACKBACK__(errorMessage) 2 | print("----------------------------------------") 3 | local output = "LUA ERROR: " .. tostring(errorMessage) .. "\n" 4 | output = output .. debug.traceback("", 2) .. "\n" 5 | print(output) 6 | print("----------------------------------------") 7 | end 8 | 9 | 10 | require("cocos.init") 11 | require("framework.init") 12 | 13 | require("app.const") 14 | -------------------------------------------------------------------------------- /src/app/scenes/LoadScene.lua: -------------------------------------------------------------------------------- 1 | local LoadScene = class("LoadScene", function() 2 | return display.newScene("LoadScene") 3 | end) 4 | 5 | function LoadScene:ctor() 6 | display.newSprite("images/login.png"):addTo(self):pos(display.cx, display.cy) 7 | end 8 | 9 | function LoadScene:onEnterTransitionFinish() 10 | local function onButtonClicked(event) 11 | dump(event) 12 | end 13 | device.showAlert("Confirm Exit", "Are you sure exit game ?", {"YES", "NO"}, onButtonClicked) 14 | end 15 | 16 | function LoadScene:onExitTransitionStart() 17 | end 18 | 19 | function LoadScene:onExit() 20 | end 21 | 22 | function LoadScene:onCleanup() 23 | collectgarbage("collect") 24 | end 25 | 26 | return LoadScene 27 | -------------------------------------------------------------------------------- /src/config.lua: -------------------------------------------------------------------------------- 1 | 2 | -- 0 - disable debug info, 1 - less debug info, 2 - verbose debug info 3 | DEBUG = 1 4 | 5 | -- display FPS stats on screen 6 | DEBUG_FPS = false 7 | 8 | -- dump memory info every 10 seconds 9 | DEBUG_MEM = false 10 | 11 | -- load deprecated API 12 | LOAD_DEPRECATED_API = false 13 | 14 | -- load shortcodes API 15 | LOAD_SHORTCODES_API = true 16 | 17 | -- screen orientation 18 | CONFIG_SCREEN_ORIENTATION = "landscape" 19 | 20 | -- design resolution 21 | CONFIG_SCREEN_WIDTH = 1280 22 | CONFIG_SCREEN_HEIGHT = 720 23 | 24 | -- 屏幕的设计尺寸 25 | DESIGN_WIDTH = 1280 26 | DESIGN_HEIGHT = 720 27 | 28 | -- auto scale mode 29 | CONFIG_SCREEN_AUTOSCALE = "FIXED_HEIGHT" 30 | CONFIG_FPS_NUMBERS = 60 -- 设置帧频 31 | 32 | GAME_ID = 111 -- 游戏ID 33 | VERSION_HOST = "2.0" -- 整包版本号 34 | 35 | GAME_CHANNEL_ID = 1 -- 游戏渠道ID,注意在对应平台会被覆盖 36 | BRANCH_ID = "master" -- 脚本代码分支ID 37 | 38 | DATA_PATH = cc.FileUtils:getInstance():getWritablePath() .. ".data/" -- 写入文件目录 39 | 40 | JIT_BIT = "" 41 | if jit then 42 | local target = cc.Application:getInstance():getTargetPlatform() 43 | if target == 0 or target == 1 or target == 2 or target == 3 then 44 | JIT_BIT = "32" 45 | elseif string.find(jit.arch, "64") ~= nil then 46 | JIT_BIT = "64" 47 | else 48 | JIT_BIT = "32" 49 | end 50 | end 51 | UPDATE_PATH = DATA_PATH .. ".loader/" -- 热更新工作目录 52 | GAME_ENTRANCE = "app.MyApp" -- APP入口,在热更新完成后会被require 53 | PRE_LOAD_ZIPS = { -- 进游戏所需要预加载的ZIP列表 54 | "framework" .. JIT_BIT .. ".zip", 55 | "game" .. JIT_BIT .. ".zip" 56 | } 57 | 58 | CC_DISABLE_GLOBAL = true -- MyApp加载完成之后禁止修改全局变量 59 | -------------------------------------------------------------------------------- /src/main.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- _oo0oo_ 3 | -- o8888888o 4 | -- 88" . "88 5 | -- (| -_- |) 6 | -- 0\ = /0 7 | -- ___/`---'\___ 8 | -- .' \\| |-- '. 9 | -- / \\||| : |||-- \ 10 | -- / _||||| -:- |||||- \ 11 | -- | | \\\ - --/ | | 12 | -- | \_| ''\---/'' |_/ | 13 | -- \ .-\__ '-' ___/-. / 14 | -- ___'. .' /--.--\ `. .'___ 15 | -- ."" '< `.___\_<|>_/___.' >' "". 16 | -- | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | -- \ \ `_. \_ __\ /__ _/ .-` / / 18 | -- =====`-.____`.___ \_____/___.-`___.-'===== 19 | -- `=---=' 20 | -- 21 | -- 22 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 | -- 24 | -- 佛祖保佑 永无BUG 25 | -- 26 | -- 27 | -- 28 | 29 | function __G__TRACKBACK__(errorMessage) 30 | print("----------------------------------------") 31 | local output = "LUA ERROR: " .. tostring(errorMessage) .. "\n" 32 | output = output .. debug.traceback("", 2) .. "\n" 33 | print(output) 34 | print("----------------------------------------") 35 | pcall(function () postError(tostring(output)) end) 36 | end 37 | 38 | collectgarbage("collect") 39 | collectgarbage("setpause", 1000) 40 | collectgarbage("setstepmul", 50000) 41 | 42 | cc.FileUtils:getInstance():addSearchPath("res") 43 | package.path = package.path .. ";src/" 44 | cc.FileUtils:getInstance():setPopupNotify(false) 45 | 46 | require("config") 47 | 48 | -- run directly. 49 | -- for _,v in ipairs(PRE_LOAD_ZIPS) do 50 | -- cc.LuaLoadChunksFromZIP(v) 51 | -- end 52 | -- require(GAME_ENTRANCE).new():run() 53 | 54 | -- run with hot update check. 55 | local configs = { 56 | bg_sprite_name = "images/login.png", -- 热更时的背景图 57 | progress_bg_name = "images/progress_bg.png", -- 热更进度条的背景资源 58 | progress_fg_name = "images/progress.png", -- 热更进度条的前景资源 59 | 60 | app_entrance = GAME_ENTRANCE, -- 游戏入口,更新完成后需要调用 61 | work_path = UPDATE_PATH, -- 更新模块的工作目录 62 | preload_zips = PRE_LOAD_ZIPS, -- 需要加载的代码zip文件列表 63 | design_width = DESIGN_WIDTH, -- 设计宽 64 | design_height = DESIGN_HEIGHT, -- 设计高 65 | seconds = 600, -- 更新超时时间 66 | 67 | zip64 = JIT_BIT, -- 64还是32位的信息 68 | } 69 | cc.LuaLoadChunksFromZIP("loader" .. JIT_BIT .. ".zip") 70 | require("loader.LoadApp").new(configs):run(true) 71 | -------------------------------------------------------------------------------- /xcode_fresh.sh: -------------------------------------------------------------------------------- 1 | # 注意:请先执行 game_build.sh.sh 编译脚本代码 2 | # 注意:请先执行 game_build.sh.sh 编译脚本代码 3 | # 注意:请先执行 game_build.sh.sh 编译脚本代码 4 | 5 | # 这个脚本是用来刷新XCODE中的资源和代码文件的, 6 | # 因为一个已知的XCODE的BUG,在编译时它不知道去更新资源文件 7 | # 这个脚本不能直接执行,只能把它的内容,复制到 Build Phases中的 Run Script 段中 8 | 9 | _GAME_BUILD_PATH="$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH" 10 | echo "_GAME_BUILD_PATH: $_GAME_BUILD_PATH" 11 | echo "PWD: $PWD" 12 | 13 | _DEST_RES_PATH="$_GAME_BUILD_PATH/res/" 14 | _DEST_SRC_PATH="$_GAME_BUILD_PATH/src/" 15 | 16 | rm -rf "$_DEST_RES_PATH" 17 | rm -rf "$_DEST_SRC_PATH" 18 | mkdir -p "$_DEST_RES_PATH" 19 | 20 | rsync -av ${SRCROOT}/../../../build/res/ "$_DEST_RES_PATH" 21 | --------------------------------------------------------------------------------