cocos2dx的python封装(python脚本引擎)

环境

cocos2dx-3.17.2

python3.7.0

目前仅做了android版本,其他平台类似,测试代码就在cpp-test里面,使用AndroidStudio打开编译运行即可。

测试用例目前只完成类SceneTest,代码在tscene.py,其他测试用例只需同样编写就行,底层有未完成的陆续补充。

近来时间非常紧张,欢迎有心人一同完善。具体内容看代码仓库README.md。

git@gitee.com:aiastefan/cocos2dx-py.git

思路

在cocos2d启动初始化glview时(Cocos2dxActivity.java),开启一个task异步拷贝python脚本代码到android应用目录,glview启动完成或者脚本拷贝完成,在AppDelegate启动处启动python脚本,代码如(py_cocos2d.cpp):

// 主循环,相当于main
void startup()
{
    /* see doc: https://docs.python.org/3.7/c-api/init.html#pre-init-safe */
    PLOGD("=====python will start up=====");
    int res = PyImport_AppendInittab("C_base",&PyInit_pybase);   //初始化模块
    res = PyImport_AppendInittab("C_2d",&PyInit_py2d);
    res = PyImport_AppendInittab("C_3d",&PyInit_py3d);
    res = PyImport_AppendInittab("C_math",&PyInit_pymath);
    res = PyImport_AppendInittab("C_physics",&PyInit_pyphysics);
    res = PyImport_AppendInittab("C_renderer",&PyInit_pyrenderer);
    res = PyImport_AppendInittab("C_ui",&PyInit_pyui);
    res = PyImport_AppendInittab("C_platform",&PyInit_pyplatform);
    res = PyImport_AppendInittab("C_extensions",&PyInit_pyextensions);
 
    // 尝试一:java层把python3.7m.MP3解压到appFile文件夹下
    //char dest[256];
    //sprintf(dest,"%s/%s", appFilePath,"python3.7m.zip");
    const char *fullPythonPath = appFilePath;
        //cocos2d::FileUtils::getInstance()->fullPathForFilename("python3.7m.MP3").c_str();// assets/python3.7.MP3
        //dest; 
    unsigned int len = 0;
    wchar_t *homePath = Py_DecodeLocale(fullPythonPath,&len);
    PLOGD("=====fullpythonpath: %s    len: %d",fullPythonPath,len);
    //Py_SetPythonHome(homePath);//把python3.7m.MP3脚本文件路径设置进去
    Py_SetPath(homePath);
 
    PLOGD("=====python will Initialize=====");
    Py_Initialize();
    if (!Py_IsInitialized()) {
        //log
        PLOGD("=====Py_Initialize failed!");
        return;
    }
    PLOGD("=====python Initialize ok=====");
 
    // 将当前路径加入解释器的搜索路径
    PyRun_SimpleString("print 'hello pycocos2d'");
    PyRun_SimpleString("import sys");
    
    char dest[256];
    sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"site-packages");// "pyscripts"
    PLOGD("=====sys.path %s",dest);
    PyRun_SimpleString(dest);
    sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pylib-dynload");// "pyscripts"
    PLOGD("=====sys.path %s",dest);
    PyRun_SimpleString(dest);
    sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts");// "pyscripts"
    PLOGD("=====sys.path %s",dest);
    PyRun_SimpleString(dest);
    sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/core");// "pyscripts"
    PLOGD("=====sys.path %s",dest);
    PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/2d");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/3d");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/math");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/ui");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/platform");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/renderer");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/physics_2d");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    // sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"pyscripts/actions");// "pyscripts"
    // PLOGD("=====sys.path %s",dest);
    // PyRun_SimpleString(dest);
    sprintf(dest,"sys.path.append('%s/%s')", appFilePath,"yourapp");// "pyscripts"
    PLOGD("=====sys.path %s",dest);
    PyRun_SimpleString(dest);
    wchar_t* pythonHome = Py_GetProgramFullPath();//Py_GetPythonHome();
    if (pythonHome != nullptr) {
        char* pythonHomeStr = Py_EncodeLocale(pythonHome,NULL);
        PLOGD("=====python home: %s",pythonHomeStr);
    }
 
    // 获取python解释器版本号
    PyObject *platform = PyImport_ImportModule("platform");
    DDD
    if (platform != nullptr) {
        PyObject *funcVersion = PyObject_GetAttrString(platform,"python_version");
        DDD
        PyObject *sVer = PyUnicode_AsEncodedString(PyEval_CallObject(funcVersion,NULL),"utf-8","~E~");
        PLOGD("=====python interpreter version: %s",PyBytes_AS_STRING(sVer));
    } else {
        PLOGD("=====import platform not found");
    }
 
    // 获取python解释器搜索路径
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *funcPath = PyObject_GetAttrString(sys,"path");
    PyObject *sPath = PyUnicode_AsEncodedString(PyObject_Repr(funcPath),"utf-8","~E~");
    PLOGD("=====python interpreter path: %s",PyBytes_AS_STRING(sPath));
 
    // 主循环,需要提供main.py脚本
    // PyObject *mMain = PyImport_ImportModule("main");
    // if (!mMain) {
    //     PLOGD("=====err not main");
    //     Py_Finalize();
    //     return;
    // }
    // DDD
    // PyRun_SimpleString("import main");
    // DDD
    // PyRun_SimpleString("main.bootstrap()");
    // DDD
 
    //pyApplicationDidFinishLaunching();
}
 
void pyApplicationDidFinishLaunching() 
{
    DDD
    PyObject *mMain = PyImport_ImportModule("main");
    if (!mMain) {
        PLOGD("=====err not main");
        Py_Finalize();
        return;
    }
    DDD
    PyRun_SimpleString("import main");
    PyRun_SimpleString("main.applicationDidFinishLaunching()");
    DDD
}

通过PyRun_SimpleString(“main.applicationDidFinishLaunching()”)调用python的main.py模块对应函数,后续逻辑都在python层写。

这里贴上tscene.py的代码,其他测试用例照样扩展。

import traceback
import C_2d
import pytest.basetest
import pylog
import pymenu
import pyaction
import pytransition
import pydirector


class SceneTest(pytest.basetest.TestSuite):
    def __init__(self):
        super().__init__()
        try:
            pylog.logi("init SceneTest")
            self.add_testcase("SceneTestScene", SceneTestScene)
        except:
            pylog.loge(traceback.format_exc())


class SceneTestScene(pytest.basetest.TestCase):
    def __new__(cls, t_suite, idx):
        return super().__new__(cls)

    def __init__(self, t_suite, idx=0):
        pylog.logi("===== SceneTestScene %d" % idx)
        super().__init__("SceneTestScene", "SceneTestLayer1")
        self.set_test_suite(t_suite)
        if idx == 0:
            layer1 = SceneTestLayer1(t_suite)  # 默认测试index=0开始
            self.addChild(layer1)
        elif idx == 1:
            layer2 = SceneTestLayer2(t_suite)
            self.addChild(layer2)
        elif idx == 2:
            layer3 = SceneTestLayer3(t_suite)
            self.addChild(layer3)


"""
以下是各实例test
"""


class SceneTestLayer1(C_2d.CLayer):
    def __init__(self, t_suite):
        super().__init__()
        self.test_suite = t_suite
        pylog.logi("init SceneTestLayer1")
        item1 = pymenu.MenuItemFont("Test pushScene", self.push_scene)
        item2 = pymenu.MenuItemFont("Test pushScene w/transition", self.push_scene_tran)
        item3 = pymenu.MenuItemFont("Quit", self.quit)
        menu = pymenu.Menu(item1, item2, item3)
        menu.alignItemsVertically()
        self.addChild(menu)
        w, h = pydirector.GetInstance().getWinSize()
        sprite = C_2d.CSprite(pytest.basetest.s_pathGrossini)
        self.addChild(sprite)
        sprite.setPosition(w-40, h/2)
        rotateby = pyaction.RotateBy(2, 360)
        repeat = pyaction.ActionInterval().RepeatForever(rotateby)
        sprite.runAction(repeat)

    def push_scene(self):
        try:
            pylog.logi("push scene")
            scene = SceneTestScene(self.test_suite, 1)
            pydirector.GetInstance().pushScene(scene)
        except:
            pylog.loge(traceback.format_exc())

    def push_scene_tran(self):
        pylog.logi("push scene transition")
        scene = SceneTestScene(self.test_suite, 1)
        dest = pytransition.TransitionSlideInT(1, scene)
        pydirector.GetInstance().pushScene(dest)

    def quit(self):
        pydirector.GetInstance().popScene()


class SceneTestLayer2(C_2d.CLayer):
    def __init__(self, t_suite):
        super().__init__()
        self.test_suite = t_suite
        pylog.logi("init SceneTestLayer2")
        item1 = pymenu.MenuItemFont("Test replaceScene", self.replace_scene)
        item2 = pymenu.MenuItemFont("Test replaceScene w/transition", self.replace_scene_tran)
        item3 = pymenu.MenuItemFont("GoBack", self.go_back)
        menu = pymenu.Menu(item1, item2, item3)
        menu.alignItemsVertically()
        self.addChild(menu)
        w, h = pydirector.GetInstance().getWinSize()
        sprite = C_2d.CSprite(pytest.basetest.s_pathGrossini)
        self.addChild(sprite)
        sprite.setPosition(40, h/2)
        rotateby = pyaction.RotateBy(1, 360)
        repeat = pyaction.ActionInterval().RepeatForever(rotateby)
        sprite.runAction(repeat)

    def replace_scene(self):
        try:
            pylog.logi("push scene")
            scene = SceneTestScene(self.test_suite, 1)
            pydirector.GetInstance().replaceScene(scene)
        except:
            pylog.loge(traceback.format_exc())

    def replace_scene_tran(self):
        pylog.logi("push scene transition")
        scene = SceneTestScene(self.test_suite, 1)
        dest = pytransition.TransitionSlideInT(1, scene)
        pydirector.GetInstance().replaceScene(dest)

    def go_back(self):
        pydirector.GetInstance().popScene()


class SceneTestLayer3(C_2d.CLayerColor):
    def __init__(self, t_suite):
        super().__init__()
        self.test_suite = t_suite
        pylog.logi("init SceneTestLayer3")

欢迎有心人加入,更欢迎实力大牛,解决部分未解之谜,以及代码封装不好的地方。直接gitee留言即可。

2赞

不知道这个东西有啥用。。。。。。。。。。。

2赞

就是可以使用python来写游戏逻辑,多一个选择吧。如果熟悉python开发效率应该可以做得更高。并且很多扩展工具使用pyqt之类的,导出的数据结构都可以不用变直接给引擎使用~

是lua 不好用 还是 TS写的不爽>?

太有意思了。Cocos 2d最开始其实是Python版本的

挖坟不会被封吧。后来人发现这个问题了。目前只有cocos2d支持python 但是怎么打包封装或者移植。要是没办法,那就直接弃坑了啊,我就指望用python玩cocos呢