cocoscreator之2048-------(2)绘制地图数据

前言

此开发过程借鉴于 Prpr_Saber大佬的文章
所以此文中会有大佬写的代码的影子,感兴趣的可以直接跳转至大佬的文章,而且大佬把原工程放在Git上了,可以直接拉取。
如果此文侵权,即刻删除。

1.State.ts

在开始之前,先分享一个实用的脚本
在这里插入图片描述
State.ts代码块

const { ccclass, property } = cc._decorator;

@ccclass
export default class State extends cc.Component
{
    public nowState: number = 0;
    //public allState: number = 0;

    start()
    {
        //this.allState = this.node.childrenCount;
    }

    //将节点的子节点,v,设置active = true,其他为false
    stateChange(v: number)
    {
        for (let i: number = 0; i < this.node.children.length; i++)
        {
            this.node.children[i].active = false;
        }
        if (v >= 0 && v < this.node.children.length)
        {
            this.nowState = v;
            this.node.children[v].active = true;
            return this.node.children[v];
        }
    }
}

将这个脚本挂在之前在场景中创建的labelRoot上在这里插入图片描述

2.实现点击左右箭头切换中间数值

修改Select.ts

import CameraManager from "../camera/CameraManager";
import State from "../common/State";

const { ccclass, property } = cc._decorator;

@ccclass
export default class Select extends cc.Component
{
    @property(cc.Node)
    backBtn: cc.Node = null;//返回按钮
    @property(cc.Node)
    startBtn: cc.Node = null;//开始游戏按钮

    @property(cc.Node)
    rightBtn: cc.Node = null;//右按钮
    @property(cc.Node)
    leftBtn: cc.Node = null;//左按钮

    @property(State)
    state: State = null;//3*3  4*4  8*8 状态改变

    private index: number = 0;//这个index代表挂State.ts的节点的当前子节点下标,初始默认值为0
    onLoad()
    {
        this.state.stateChange(this.index);//这条语句的作用就是,将挂载State.ts的节点,第一个子节点设为真,其他都为false
        //因为这里在引擎里直接创建的button组件,所以.on('click')而不是.on('touchend')
        this.backBtn.on('click', () =>
        {
            CameraManager.getInstance().moveCamera(-1)
        }, this)

        this.rightBtn.on('click', () =>
        {
            if (this.index < 2)
            {
                this.index++;
                this.state.stateChange(this.index)
            }
        }, this)
        this.leftBtn.on('click', () =>
        {
            if (this.index > 0)
            {
                this.index--;
                this.state.stateChange(this.index)
            }
        }, this)
    }
}

在这里插入图片描述
这时候运行游戏点击左右箭头就可以切换中间的数值了

3.制作Play节点

因为play节点的绘制信息由select节点选择的几乘几的数组决定的,所以可以将play节点制成一个预制体,每次在select
点击开始按钮时,实例化一个预制体
  1. 修改Select.ts,在Select.ts脚本中加入以下代码块

在这里插入图片描述
在此之前,需对Play.ts加入Init()方法

const { ccclass, property } = cc._decorator;

@ccclass
export default class Play extends cc.Component
{
    @property(cc.Node)
    backBtn: cc.Node = null;//返回按钮

    init(v: number)
    {
        //TODO:后续使用
    }
    onLoad()
    {
        this.backBtn.on('click', () =>
        {
            this.node.destroy();
            CameraManager.getInstance().moveCamera(-1)
        }, this)
    }
}

  1. 修改场景节点,并制作一个playPre预制体。itemParent节点设置为600,600,居中,Anchor角度设置为(0,1),Anchor一定要设置好,不然会有问题
    在这里插入图片描述
    在这里插入图片描述

3.制作格子预制体,加入生成逻辑,对象池

  1. 制作展示格子
    在这里插入图片描述
    因为格子是一直在生成的,所以使用对象池。
  2. 编写对象池脚本PoolManager.ts
    在这里插入图片描述
    这里的ActionManager和DataManager随后介绍
    PoolManager.ts代码块

export default class PoolManager 
{
    constructor()
    {
        this._blockPool = new cc.NodePool();
        this._nodeList = new Array<cc.Node>();
    }
    _blockPool: cc.NodePool
    _nodeList: cc.Node[]
    prefab: cc.Prefab

    static instance: PoolManager
    static getInstance(): PoolManager
    {
        this.instance = this.instance ? this.instance : new PoolManager()
        return this.instance
    }

    //初始化节点池大小,3*3大小就为9,4*4就为16,8*8就为64
    initPool(prefab: cc.Prefab, size: number)
    {
        for (let i = 0; i < size; i++)
        {
            let block = cc.instantiate(prefab);
            this._blockPool.put(block);
        }
        this.prefab = prefab;
    }
    //从节点池中拿取一个节点,节点池中没有,就重新生成一个
    getBlock(): cc.Node
    {
        let block = null;
        if (this._blockPool.size() > 0)
        {
            block = this._blockPool.get()
        }
        else
        {
            block = cc.instantiate(this.prefab)
        }
        return block;
    }
    //将节点保存在一个数组内
    saveNode(node: cc.Node)
    {
        this._nodeList.push(node)
    }
    //清除节点数组,但不清除节点池
    clearNodeList()
    {
        for (let node of this._nodeList)
        {
            this._blockPool.put(node)
            this._nodeList = [];
        }
    }
    //节点池和节点数组全部清除
    clearPool()
    {
        this._nodeList = [];
        this._blockPool.clear();
    }
}

  1. 添加数据处理类DataManager.ts
/**
 * 核心数据处理在这个脚本
 */
export default class DataManager
{
    private map: number[][] = [];//地图数据

    static instance: DataManager

    static getInstance(): DataManager//实例化
    {
        this.instance = this.instance ? this.instance : new DataManager();
        return this.instance
    }

    initMap(_size: { row: number, col: number }, value: number)
    {
        this.map = [];//每次初始化的时候将其置为空
        for (let row = 0; row < _size.row; row++)
        {
            this.map.push([])
            for (let col = 0; col < _size.col; col++)
            {
                this.map[row][col] = value;
            }
        }
        console.log("初始化数据", this.map)//初始化完成后,查看地图数据
    }
    //添加一个格子信息,即将数组中不为0的其中一个数值改变
    addRandom(): boolean
    {
        let points: cc.Vec2[] = [];//保存数组中不为0的横纵坐标
        let hasNext: boolean = false;//是否能生成下一个

        this.traversalTwoArray(this.map, (row, col) =>
        {
            if (this.map[row][col] == 0)
            {
                points.push(cc.v2(row, col))
                hasNext = true;
            }
        })
        if (hasNext)
        {
            let index = Math.floor(Math.random() * points.length)
            let row = points[index].x;
            let col = points[index].y
            this.map[row][col] = 2;//TODO:这个值后续修改,添加限制,生成2或者4,而不是一味生成2
        }
        return hasNext
    }
    //获得地图数据
    getMap(): number[][]
    {
        // console.log("getMap", this.map)
        return this.map
    }
    //遍历二维数组,这个方法自认为挺好用的,可以认真看一下
    traversalTwoArray<Type>(arr: Type[][], callback: (row: number, col: number) => void)
    {
        for (let row = 0; row < arr.length; row++)
        {
            for (let col = 0; col < arr[row].length; col++)
            {
                callback(row, col)
            }
        }
    }
}
  1. 节点行为类ActionManager.ts
import DataManager from "./DataManager";
import PoolManager from "./PoolManager"

/**
 * 此脚本绘制场景节点信息,包括展示,移动。。。
 */

const Color = {
    2: cc.color(237, 241, 21, 255),
    4: cc.color(241, 180, 21, 255),
    8: cc.color(171, 241, 21, 255),
    16: cc.color(149, 160, 216, 255),
    32: cc.color(187, 149, 216, 255),
    64: cc.color(216, 149, 209, 255),
    128: cc.color(28, 118, 156, 255),
    256: cc.color(16, 74, 99, 255),
    512: cc.color(168, 85, 25, 255),
    1024: cc.color(236, 122, 38, 255),
    2048: cc.color(236, 86, 33, 255)
}
export default class ActionManager
{
    constructor(itemParent: cc.Node)
    {
        this.itemParent = itemParent;
    }
    private itemParent: cc.Node;//格子的父节点
    private itemWidth: number//格子节点宽
    private itemHeight: number//格子节点高

    //绘制地图
    draw(_map: number[][])
    {
        PoolManager.getInstance().clearNodeList();

        this.itemWidth = this.itemParent.width / _map.length
        this.itemHeight = this.itemParent.height / _map.length
        DataManager.getInstance().traversalTwoArray(_map, (row: number, col: number) =>
        {
            if (_map[row][col] !== 0)//初始地图数据为0
            {
                let item = PoolManager.getInstance().getBlock();
                item.parent = this.itemParent;

                //根据地图长度设置节点大小
                item.setContentSize(this.itemWidth, this.itemHeight);
                //这里的大小会有一个问题,假设父节点itemParent的width和height为600,
                //传入的map长度为3,那么width和height为200,按照一定的位置排开,正好是
                //铺满的状态,所以在这里可以在item预制体下加一个单色子节点,item本身大小就为200
                //他的单色子节点大小我们定位190,或者其他一些更小的值,这样展示的时候就不会铺满
                //整个itemParent,相互之间会有间隔

                //这里一定要注意下,item在引擎中设置时,要把单色子节点放在第一个位置
                item.children[0].setContentSize(this.itemWidth - 10, this.itemHeight - 10);
                item.children[0].color = Color[String(_map[row][col])]
                item.children[1].getComponent(cc.Label).string = String(_map[row][col]);

                item.name = row + "" + col//这里作用是移动的时候能在itemParent下找到节点
                item.x = this.itemWidth / 2 + this.itemWidth * col;//这里的位置要自己推一下
                item.y = -this.itemHeight / 2 - this.itemHeight * row;

                PoolManager.getInstance().saveNode(item)
            }
        })
    }
}
  1. Play.ts脚本修改如下
    在这里插入图片描述
    在预制体playPre中将itemParent节点和item预制体分别拖入,这时候我们就可以看到游戏中生成格子了

8*8
3*3
4*4

后续继续写其他逻辑

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值