All Projects → ustchcl → CocosProjectTemplate

ustchcl / CocosProjectTemplate

Licence: other
一个Cocos Creator使用的应用基础框架 微信小游戏版本https://github.com/ustchcl/CocosProjectTemplateWx

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects
HTML
75241 projects
CSS
56736 projects

Projects that are alternatives of or similar to CocosProjectTemplate

r2dbc-proxy
R2DBC Proxying Framework
Stars: ✭ 108 (+440%)
Mutual labels:  reactive
cl-gserver
Actor framework featuring actors and agents for easy access to state and asynchronous operations.
Stars: ✭ 121 (+505%)
Mutual labels:  reactive
purescript-outwatch
A functional and reactive UI framework based on Rx and VirtualDom
Stars: ✭ 33 (+65%)
Mutual labels:  reactive
ReactiveAPI
Write clean, concise and declarative network code relying on URLSession, with the power of RxSwift. Inspired by Retrofit.
Stars: ✭ 79 (+295%)
Mutual labels:  reactive
onix
A reactive configuration manager designed to support Infrastructure as a Code provisioning, and bi-directional configuration management providing a single source of truth across multi-cloud environments.
Stars: ✭ 89 (+345%)
Mutual labels:  reactive
reactiverse
The Reactiverse main entry point
Stars: ✭ 26 (+30%)
Mutual labels:  reactive
sample-spring-reactive
sample reactive microservices using spring 5, spring boot, spring webflux and project reactor
Stars: ✭ 26 (+30%)
Mutual labels:  reactive
bindkit
Two-way data binding framework for iOS. Only one API to learn.
Stars: ✭ 13 (-35%)
Mutual labels:  reactive
callbag-subscribe
A callbag sink (listener) that connects an Observer a-la RxJS. 👜
Stars: ✭ 17 (-15%)
Mutual labels:  reactive
DashIntro
A quick intro to Dash made for the PyData event in Zurich
Stars: ✭ 57 (+185%)
Mutual labels:  reactive
firing balls
Physical pinball game with cocos creator 小游戏【不停歇的球】源码
Stars: ✭ 120 (+500%)
Mutual labels:  cocoscreator
s2
A function for reactive web UI.
Stars: ✭ 43 (+115%)
Mutual labels:  reactive
akka-stream-kafka-template.g8
Template for Akka Streams & Kafka. Default impl: mirror a topic onto another one
Stars: ✭ 14 (-30%)
Mutual labels:  reactive
bassdrum
reactive, type safe components with preact and rxjs.
Stars: ✭ 44 (+120%)
Mutual labels:  reactive
bound
Data-binding made easy
Stars: ✭ 21 (+5%)
Mutual labels:  reactive
lila-ws
Lichess' websocket server
Stars: ✭ 99 (+395%)
Mutual labels:  reactive
muon-java
Muon Core for the JVM. APIs and Microservices taken to the next level
Stars: ✭ 18 (-10%)
Mutual labels:  reactive
WpfExtensions
Some syntactic sugar for Wpf development.
Stars: ✭ 128 (+540%)
Mutual labels:  reactive
agera
Reactive Programming for Android
Stars: ✭ 7,267 (+36235%)
Mutual labels:  reactive
akka-cookbook
提供清晰、实用的Akka应用指导
Stars: ✭ 30 (+50%)
Mutual labels:  reactive

一个简单的框架

仿照Elm, Halogen那样抽象事件,数据和渲染。

mobx + cocos2d 2.4.3

Mobx + Cocos2d 加了一个todo样例

  1. Diff示例, list的数据渲染
  2. Action State Props细化
  3. 父子组件交互问题

新版本预览

  • 使用mobx管理状态, 感觉比rxjs更舒服好用,
  • 自己定义action配合ramda 更新状态 嗯姆
import { modify, TimeInfo, timeInfo, formatNum } from "../basic/BaseFunction";
import { BaseComponent } from "../basic/BaseComponent";
import {max, subtract, pipe, min, add, __} from "ramda";

import { observable, autorun, computed } from "mobx";

const {ccclass, property} = cc._decorator

class State {
    @observable
    count: number = 0;

    @observable
    timestamp: number = Date.now();

    @computed
    get time() {
        return timeInfo(this.timestamp);
    } 
}

type Action 
    = ["Inc"]
    | ["Dec"] 
    | ["Set", number]
    

@ccclass
export class CounterExample extends BaseComponent<State, Action> {
    @property(cc.Button)
    minusButton: cc.Button = null;
    @property(cc.Button)
    plusButton: cc.Button = null;
    @property(cc.Label)
    contentLabel: cc.Label = null;
    @property(cc.Label)
    timeLabel: cc.Label = null;
    @property(cc.Button)
    maxButton: cc.Button = null;

    readonly MAX_SIZE = 999;

    start () {
        this.onTouchEnd(this.minusButton.node, ["Dec"]);
        this.onTouchEnd(this.plusButton.node, ["Inc"]);
        this.onTouchEnd(this.maxButton.node, ["Set", this.MAX_SIZE]);
        this.state = new State();
        this.subs = [
            autorun(() => this.renderCounter(this.state.count)),
            autorun(() => this.renderTimeInfo(this.state.time))
        ]
        this.schedule(() => this.setState({"timestamp": Date.now()}), 1);
    }

    eval (action: Action) {
        switch (action[0]) {
            case "Dec": {
                this.modify({"count": pipe(subtract(__, 1), max(0))})
                break;
            }
            case "Inc": {
                this.modify({"count": pipe(add(1), min(this.MAX_SIZE))});
                break;
            }
            case "Set": {
                this.setState({"count": action[1]});
                break;
            }
        }
    }

    renderTimeInfo(ti: TimeInfo) {
        this.timeLabel.string = `${formatNum(ti.hour, 2)}:${formatNum(ti.minute, 2)}:${formatNum(ti.seconds, 2)}`;
    }

    renderCounter(count: number) {
        this.contentLabel.string = String(count);
    }
}

更新 07.08

1. 添加subs
BaseComponent在destroy时,会将subs全部unsubscribe

// BaseComponent.ts
export abstract class BaseComponent<State, Action extends Type<any, any>> extends cc.Component implements Component<State, Action> {
    subs: Array<Subscription> = [];

    onDestroy() {
        this.subs.forEach(sub => sub.unsubscribe());
    }
}

// Example.ts
@ccclass 
export class CounterExample extends BaseComponent<State, Action> {
    start () {
        this.subs = [
            this.state.count.subscribe({ next: count => this.render(count)})
        ]
    }
}

2. safeRemove修改
现在safeRemove默认调用node.destroy()

新更新

  1. 二次确认框
// 此处定义,需要二次确认框的类型及其可能的初始化参数
export type SecondConfirmType 
    = TypeUnit<"ExitSecondConfirm">
    | Type<"SaveConfig", Pair<number, number>>

export async function showSC(type: SecondConfirmType): Promise<boolean> {
    let sc: SecondConfirm = null;
    switch (type.typeName) {
        case "ExitSecondConfirm": {
            let prefab = await ResUtils.prefab("exitSecondConfirm");
            let node = cc.instantiate(prefab);
            sc = node.getComponent(ExitSecondConfirm);
            GlobalEnv.getInstance().dispatchAction(Action("OpenPanelWithNode", node));
            break;
        }

        case "SaveConfig": {
            let prefab = await ResUtils.prefab("saveConfig");
            let node = cc.instantiate(prefab);
            let saveConfig = node.getComponent(SaveConfig);
            saveConfig.init(type.value.fst, type.value.snd);
            sc = saveConfig;
            GlobalEnv.getInstance().dispatchAction(Action("OpenPanelWithNode", node));
            break;
        }
    }

    return new Promise<boolean>(function(resolve) {
        sc.cancelBtn.node.on(TOUCH_END, () => {
            safeRemove(sc.node);
            resolve(false);
        });
        sc.yesBtn.node.on(TOUCH_END, () => {
            safeRemove(sc.node);
            resolve(true);
        });
    });
}

// usage
async function sthOnClick () {
    if (await showSC(Action("SaveConfig", mkPair(param1, param2)))) {
        // 用户点击确认
    } else {
        // 用户点击取消
    }
}
  1. BaseFunction.ts
    更新一系列函数

  2. 去掉了无用的action

  3. Json载入测试

// test.json
{
    "first": {
        "id": 1,
        "content": "one"
    }
}
import * as test from "./test.json" // 或者 import test from "./test.json"

console.log(test.first.id === 1); // OK
console.log(test.first.content === 1); // Error

测试结果 CocosCreator编译会报错
但是编译到微信小游戏里面可以正常载入Json

在 tsconfig.json compilerOptions里面添加
"resolveJsonModule": true

关于消息

传统Message传递

// pureMVC
class XXX extends puremvc.Mediator implements puremvc.IMediator {
    public static NAME: string = "XXXMediator";

    public constructor(viewComponent: any) {
        super(GameMenuMediator.NAME, viewComponent);
    }


    public listNotificationInterests(): Array<any> {
        return [GameProxy.SCORE_UPDATE, GameProxy.SCORE_RESET];
    }
    
    public handleNotification(notification: puremvc.INotification): void {
        var data: any = notification.getBody(); // 这个地方是any
        switch (notification.getName()) {
            case GameProxy.SCORE_UPDATE: {
               break;
            }
            case GameProxy.SCORE_RESET: {
               break;
            }
        }
    }
}

消息传递只是定义了消息的名字,对消息的内容的类型并没有限定,需要靠约定。
自己定义消息的类型和消息体的类型

export type GlobalAction
    = Type<"OpenPanelWithNode", cc.Node>  // 定义消息体类型: cc.Node
    | TypeUnit<"BackToMainPage">  // 消息内容为unit


function handleAction(action: GlobalAction) {
    switch (action.typeName) {
        case "OpenPanelWithNode": { // 消息类型可以提示,写错会提示
            let node = action.value; // node的类型会被自动推断为cc.Node, 调用不是cc.Node的方法会报错
            break;
        }
        case "BackToMainPage": {
            // action.value 为 unit
            break;
        }
    }
}
  • eval 处理事件, 根据事件更新state
    • 所有的逻辑,case处理,应仅有次数可以更新state
  • render 根据state渲染,可以由多个绑定到State里面各个Behavior上
  • state 明确定义的State, 内部由一个以上的BehaviorSubject构成
  • Action 对事件的统一抽象。需要自己定义。 定义该组件可能会产生的交互行为,包括
    • UI交互
    • 网络请求
    • 定时器
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)

main =
  Browser.sandbox { init = init, update = update, view = view }

type Msg = Increment | Decrement

type alias Model = Int  -- state

init : Model
init = 0

update msg model =      -- eval
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

view model =            -- render
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]
import { Type, TypeUnit, ActionUnit, Action } from "../basic/Types";
import { BehaviorSubject } from "rxjs";
import { __, add, always } from "ramda"
import { modify } from "../basic/BaseFunction";
import { BaseComponent } from "../basic/BaseComponent";

const {ccclass, property} = cc._decorator

interface State {
    count: BehaviorSubject<number>;
}

type Action 
    = TypeUnit<"Inc">
    | TypeUnit<"Dec"> 
    | Type<"Set", number>

@ccclass
export class CounterExample extends BaseComponent<State, Action> {
    @property(cc.Button)
    minusButton: cc.Button = null;
    @property(cc.Button)
    plusButton: cc.Button = null;
    @property(cc.Label)
    contentLabel: cc.Label = null;
    @property(cc.Button)
    maxButton: cc.Button = null;

    readonly MAX_SIZE = 999;

    start () {
        this.onTouchEnd(this.minusButton.node, ActionUnit("Dec"));
        this.onTouchEnd(this.plusButton.node, ActionUnit("Inc"));
        this.onTouchEnd(this.maxButton.node, Action("Set", this.MAX_SIZE));
        this.state = {
            count: new BehaviorSubject<number>(200)
        };
        this.subs = [
            this.state.count.subscribe({ next: count => this.render(count)})
        ]
    }

    render(count: number) {
        this.contentLabel.string = String(count);
    }

    eval (action: Action) {
        switch (action.typeName) {
            case "Dec": {
                let count = this.state.count.getValue();
                if (count > 0) {
                    modify(this.state.count, add(__, -1))
                }
                break;
            }
            case "Inc": {
                let count = this.state.count.getValue();
                if (count < this.MAX_SIZE) {
                    modify(this.state.count, add(__, 1))
                }
                break;
            }
            case "Set": {
                modify(this.state.count, always(action.value));
                break;
            }
        }
    }
}

定义ADT类型

type Type<T, U> = {typeName: T, value: U}
type TypeUnit<T> = Type<T, Unit>

type RemoteData<Ok, Err> 
    = TypeUnit<"NotAsked"> 
    | TypeUnit<"Loading"> 
    | Type <"Success", Ok> 
    | Type <"Failure", Err>

参考Purescript/Haskell定义ADT

data RemoteData e a
  = NotAsked
  | Loading
  | Failure e
  | Success a

Cocos Creator V2.0 以上的 Shader

首先一个cocos creator(version >= 2.0) 用的shader类型如下

interface Shader {
    name: string,
    defines: Array<{ name : string }>,
    vert: string,
    frag: string,
}

const DefaultVert: string  = 
`
uniform mat4 viewProj;
uniform mat4 model;
attribute vec3 a_position;
attribute vec2 a_uv0;
varying vec2 uv0;
void main () {
    mat4 mvp;
    mvp = viewProj * model;
    vec4 pos = mvp * vec4(a_position, 1);
    gl_Position = pos;
    uv0 = a_uv0;
}
`

fragmentShader与shadetoy上不一样的地方有 首先,shader的输入变量如下: 可自行定义,需要在glsl中修改

let mainTech = new renderer.Technique(
    ['transparent'],
    [
        { name: 'texture', type: renderer.PARAM_TEXTURE_2D },   // 纹理 iChannel0, shadertoy上使用iChannel0的地方请替换为texture
        { name: 'color', type: renderer.PARAM_COLOR4 },         // 当前的颜色值
        { name: 'pos', type: renderer.PARAM_FLOAT3 },           // iResolution
        { name: 'size', type: renderer.PARAM_FLOAT2 },          // size, 可用来计算fragCoord
        { name: 'time', type: renderer.PARAM_FLOAT },           // iTime
        { name: 'num', type: renderer.PARAM_FLOAT }             
    ],
    [pass]
);

其次

// 开头的变量声明, 变量名字可以使用shadertoy的着色器输入变量,不过在ShaderMaterial中需修改
// 其mainTech对应的值
uniform sampler2D texture;
uniform vec3 pos;
uniform float time;
uniform vec2 size;
varying vec2 uv0; // 这个是额外的变量, 用于计算fragCorrd, 应该是引擎传入的当前坐标的相关信息;

最后

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // code
}

void main() {
    // 首先要先计算出所使用的fragCoord
    vec2 fragCoord = vec2(uv0.x * size.x - 0.5 * size.x, 0.5 * size.y - uv0.y * size.y);
    // code
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].