Puzzle game developed by cocos 《InOutPath》

Hello hallo, we are the development team of the indie game InOutPath, and as the development of the game is getting better and better, we have now made the Steam page of InOutPath available.

The address is as follows: Switch on Steam InOutPath (steampowered.com)

A lot of emotions as a member of an indie game team. Thank you to the cocos team for providing a great engine that allowed us to build this game without technical constraints, and to use our creative space and abilities as we pleased.

We are an adventure puzzle game with 7 big chapters and 300+ small chapters. It contains 7 different styles of scenes, including ocean, water, bamboo forest, etc., and more than 20 different puzzle mechanisms.

Our game needs to satisfy the imagination of the creative team in terms of programme implementation, which is inseparable from the powerful visual editor capability provided by cocos.

I would like to thank the cocos team once again for their outstanding abilities. Three bows.

Without further ado, let’s get to the technical forum.

First of all, the whole driver of the game is divided into the data part and the display part. The data part is based on the cocos editor plugin for data construction. Thanks a lot to cocos team 2youyou2 for the idea.

We built a comfortable path planning tool based on cce.gizmos api. ps: cce api is the editor namespace API of cocos editor api, which encapsulates a lot of operations for cocos editor.

The API is as follows:

Because the path data is independent of the object model, this makes it very easy to do a lot of creation before the art resources are ready without increasing the subsequent development costs, and the paths are simple data without any runtime loss.

Let’s start by looking at a small level.

The white line in the middle of the path is our level path. He takes on the logic of almost the entire game. Whether it’s a small map or a mega map, it can be a handful.

Whether your path needs to be rotated, zoomed or panned it can be very easy on the line of sight, whether your path is a straight line, a curve or something physically non-existent.

Due to our stylised level atmosphere. So we have 9 configured completely different scene data, we will use the scene data to sync to the current scene when loading different styles, as we don’t load other scenes while the game is running. Other than the MAIN scene, the other scenes are just an atmosphere config table. It contains the main directional light, fog effects and other kinds of parameters.


This way we can run through all styles of atmosphere inside a scene.

Next is the post-processing part, since the new version of cocos’ post-processing framework doesn’t meet our project’s needs in terms of performance, we continue to continue the post-processing architecture of our other RPG.

The post-processing techniques we are currently using are:

Similarly, we do level control for post-processing when loading different styles.

Here I’ll talk a little bit about the way we get the depth map.

Since we don’t want to render the scene once to get the depth map, we also don’t need a depth map for a specific angle, such as the top depth map for a rain mask. So we just need to copy the depth map and use it before rendering the transparent objects. If you ask me why we don’t just use it, we need to copy it. That’s because there will be a read-on-write problem with gl.

The key code is as follows:

Next is all GrabPass, we need to do the fluctuation of the underwater part and the normal rendering of the water part.

Also a full screen copyPass to get it done.


It’s about copying over the already rendered results before rendering transparent objects.

Then it’s time to publish steam which needs to be packaged for desktop.

Here we are using electron+web mobile.

The steam sdk library we use is whaqzhzd/steamworks.js: A steamworks implementation for nw.js/electron games (github.com)

The difference between this library and the original is that we’re plugging into the new version of the steam p2p sdk, which we need to use for another multiplayer RPG.

A word about electron’s encryption. Since the web mobile code is all bare bones. This means that if someone unpacks it with asar, they can see the entire source code at no cost. So we implemented a very simple encryption based on rust.

Since we know electron can use node files as startup files, the first thing to do is to change the startup file to an encrypted one.

So the first step is to change the startup file to an encrypted node file.

The source code is then encrypted using the electron-packager api:

Encryption and then rust inside the source code loading is intercepted and then decrypted. Here first we block the debugging of the application.

Then it’s a matter of overriding node’s _compile function inside rust using napi. This allows the js engine to load the decrypted code.

The decryption function is the inverse of the previous encryption function.

Note that since cocos uses systemjs, we also need to intercept the systemjs createScript function.

At this point a simple encryption is complete. This encryption is very elementary and can be made only a little more difficult. But the essence remains unchanged.

Finally I put a few game pictures for you to see. I hope you can help me with the wish list. Thank you all.

InOutPath (steampowered.com)

whaqzhzd/electron-asar-encrypt (github.com)



This looks really good. I’ve added it to my steam wishlist. All the best guys I’m looking forward for the release.

I’ve not seen many 3D games developed in Cocos Creator especially a pc game as it’s fairly new compared to other 3D engines available in the market. Also Thanks for sharing the technical details it’s really insightful.

thanks, Cocos Creator has a strong 3D technology capability after the 3.8 version. Let’s build more fun games with it. unlock its potential.

1 Like

Very good read. How big is the team and how long was the development time?

I am interested in this part. Would be nice to dig a bit deeper into it

We built a comfortable path planning tool based on cce.gizmos api. ps: cce api is the editor namespace API of cocos editor api, which encapsulates a lot of operations for cocos editor.

I also dont understand about the need for encryption. Can you explain it a bit more? Does it mean if you do not do encryption, when you package the game for steam, someone can get the source code with no cost?

Also, can you share a bit more about optimization for 3d scene like this? Is there anything specific?

Hello, regarding the first part I just upgraded spline-tool to version 3.8.1. Our path planning/pathfinding mainly relies on the spline-tool which relies on the cce api. my upgraded code repository is in spline-tool, the original repository can be Good to learn part of the cce api usage, look into it if you are interested.

As for why it is encrypted, it is as you said, because we package it using electron, which for reasons we all know, just uses asar technology to merge files together. For anyone who understands electron and asar, it is easy to see your code using the asar unpack command. I this encryption is very simple. It can only do some basic defence.

Thanks so much for sharing this extensive experience while building for native platforms, it definitely will encourage more people from the community to believe in the tool’s capabilities and trust to pursue more complex projects.

I’m especially grateful for the example and code base for steamworks.js, I would love to have a more complete guide on how to implement such lib together with Cocos.

One question that still lurks in my mind regarding the PC Native implementation, is that by my experience I had a really tough time controlling the screen size and resolution during runtime (in fact, I was not able to figure this one out) as also how to deal with keyboard inputs.

Would you be so kind to share your experience while implementing such topics?

Once more, thanks for this contribution!

Of course, I’d be happy to. When it comes to native resolution switching implementation on PC. Please excuse me. With my shallow cocos knowledge reserve, I know cocos itself does not provide an out-of-the-box API for this. So. Here we need to implement it ourselves. My implementation is as follows:

import {
} from 'cc';

screen.windowSize = size(width, height);

First is to adjust the windowSize size. For example, change 19201080 to 800600.

screen-adapter Modify the windowSize implementation as follows:

	public set windowSize(size: Size) {
		if (size.width < 1920 && size.height < 1080) {
		} else if (size.width == 1920 && size.height == 1080) {

		const wMgr = jsb.ISystemWindowManager.getInstance();
		//native\cocos\platform\interfaces\modules\ISystemWindow.h ISystemWindow static constexpr uint32_t mainWindowId = 1;
		const mWin = wMgr.getWindow(1);
		mWin.setWindowSize(size.width, size.height);

cocos-engine/native/cocos/platform/interfaces/modules/ISystemWindow.h at v3.8.3 · cocos/cocos-engine (github.com) Append virtual function:

virtual void setWindowSize(uint32_t width, uint32_t height) = 0;

emoty、ios、java、linux、mac、win32 Implement the function separately。

For example, on win32 it is:

void SystemWindow::setWindowSize(uint32_t width, uint32_t height) {
     SDL_SetWindowSize(_window, width, height);

Then use auto jsb to generate glue code:

static bool js_cc_ISystemWindow_setWindowSize(se::State& s)
    CC_UNUSED bool ok = true;
    const auto& args = s.args();
    size_t argc = args.size();
    cc::ISystemWindow *arg1 = (cc::ISystemWindow *) NULL ;
    uint32_t arg2 ;
    uint32_t arg3 ;
    if(argc != 2) {
        SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2);
        return false;
    arg1 = SE_THIS_OBJECT<cc::ISystemWindow>(s);
    if (nullptr == arg1) return true;
    ok &= sevalue_to_native(args[0], &arg2, s.thisObject());
    SE_PRECONDITION2(ok, false, "Error processing arguments");
    ok &= sevalue_to_native(args[1], &arg3, s.thisObject());
    SE_PRECONDITION2(ok, false, "Error processing arguments");
    return true;

cls->defineFunction("setWindowSize", _SE(js_cc_ISystemWindow_setWindowSize)); 

Remember. If the glue code is not generated, the setWindowSize function cannot be called.。

At this point, you have implemented the dynamic resolution modification function. What you need to do is call it when the player triggers this operation:

screen.windowSize = size(width, height);

As for keyboard listening, this should be a cross-platform implementation. It seems there is no problem, right?

1 Like

In fact, there are many 3D games made with Cocos Creator now.
The team is preparing a video to show some good projects made with Cocos Creator in 2023, will be released soon.
Just stay tuned.

1 Like

Wow, thanks so much!

Indeed, I was not able to find much information about it and got confused if such logic would need to be done on the C++ side of the project or if the TS codebase would solve it.

Now I know :slight_smile:

Regarding the keyboard key, I feel that the event-based system does not always work great while I was testing it, so I was curious if you had to do any fine-tuning on your side, but seems you didn’t.

Yes, I’m not quite clear on what situations the keyboard key listeners did not work for you?

Looks great!