更多源码请扫码关注公众号
编者荐语:
不辜负每一份热爱
以下文章来源于懒惰的An ,作者懒惰的An
《合成大西瓜》原版开发者,cocos游戏开发小辣鸡,会发一些自己做的游戏源码或功能教程,搬运一些大老的技术分享~因为我比较懒,所以也可能不发~
大家好,我是An~wx公众号也申请了半年了,第一次写文,水平很低,凑活看吧
大西瓜火了,这两天也是在网上看到了各种模仿的大西瓜作品,可能也有朋友想知道这么简单的小游戏是怎么做的,今天我们也来模仿还原一下原版手感的合成大西瓜
正题:
第一步:打开CocosCreator,今天我们用2.4.3来制作
新建工程,导入素材~~
因为是竖屏游戏,Canvas参数设置成720*1288
在Canvas下加入一个新的精灵节点Sprite,size设置成720*1280,命名为BG
再把地板素材拖到BG下面,名字为Floor,在BG新建两个新的单色精灵Sprite节点,大小设置为20*1280,放在背景图片两侧
同时给Floor,WallLeft,WallRight挂上RigidBody刚体组件和PhysicsBoxCollider碰撞器组件,RigidBody组件的Type设置为Static
这样一个场景就做好了
下面做一个水果的预制体
在Canvas下新建一个空节点,用于存放我们下落的水果 名字就叫FruitNode,再新建一个空节点叫TargetFruitNode,用于存放我们当前控制的水果节点,我们从水果素材里随便拿出一个放在FruitNode节点下,改名叫FruitPre,同时给FruitPre加上RigidBody和PhysicalCircleCollider,参数也设置好
还要记得设置好分组 墙壁设置成wall,水果设置成fruit
然后把节点拖到Perfab文件夹里,这样预制体就做好了
然后把场景里的FruitPre删掉
这样基本工作就做好了
下面我们写代码
在scripts创建一个ts脚本,名字为GameManager,脚本设置成单例
public static Instance: GameManager = null;
onLoad () {
if (GameManager.Instance != null) {
GameManager.Instance.destroy();
}
GameManager.Instance = this;
cc.director.getPhysicsManager().enabled = true;//物理游戏,开启物理 }
脚本挂在Canvas上,首先获取到节点和预制体
@property(cc.Node)
fruitNode: cc.Node = null;
@property(cc.Node)
targetfruitNode: cc.Node = null;
@property(cc.Prefab)
fruitPre: cc.Prefab = null;
targetFruit: cc.Node = null;
然后写第一个方法,在屏幕上方生成一个水果,直接上代码
createOneFruit(_pos: cc.Vec2) {
let fruit = cc.instantiate(this.fruitPre);//实例化一个预制体
fruit.setParent(this.targetfruitNode)//更改fruit父节点
fruit.setPosition(_pos);//设置坐标
fruit.setScale(0)//出生时scale设置为0
fruit.getComponent(cc.RigidBody).type = cc.RigidBodyType.Static;//刚体类型设置为Static防止下落
fruit.getComponent(cc.PhysicsCircleCollider).radius = 0;//碰撞器半径先设置位0,下落时再改到相应水果大小
fruit.getComponent(cc.PhysicsCircleCollider).apply();//保存碰撞器更改的参数
//水果生成时执行一个缩放动作
cc.tween(fruit)
.to(0.5, { scale: 1 }, { easing: 'backOut' })
.call(() => {
this.targetFruit = fruit;//将我们控制的水果targetFruit设置为刚生成的fruit
})
.start() }
在start()里调用一下
start() {
this.createOneFruit(cc.v2(0,500))
}
运行:
这样只能生成固定的一个水果,我们应该在代码里随意生成我们想要的水果,该怎么写呢。首先声明一个SpriteFrame数组,存放我们所有的水果素材
@property(cc.SpriteFrame)
Allfruit: cc.SpriteFrame[] = [];
然后改一下生成水果的代码
/**
* 生成一个水果/只用于生成上方的水果
* @param _fruitNum 水果类型 0是葡萄 以此类推
* @param _pos 水果生成的位置
*/
createOneFruit(_fruitNum: number, _pos: cc.Vec2) {
let fruit = cc.instantiate(this.fruitPre);//实例化一个预制体
fruit.setParent(this.targetfruitNode)//更改fruit父节点
fruit.getComponent(cc.Sprite).spriteFrame = this.Allfruit[_fruitNum];//更改fruit的图片
fruit.getComponent("FruitCollision").fruitNumber = _fruitNum;//fruit碰撞回调脚本,里面有一个当前水果类型 如果是葡萄 fruitNumber为0 用于相同合成检测
fruit.setPosition(_pos);//设置坐标
fruit.setScale(0)//出生时scale设置为0
fruit.getComponent(cc.RigidBody).type = cc.RigidBodyType.Static;//刚体类型设置为Static防止下落
fruit.getComponent(cc.PhysicsCircleCollider).radius = 0;//碰撞器半径先设置位0,下落时再改到相应水果大小
fruit.getComponent(cc.PhysicsCircleCollider).apply();//保存碰撞器更改的参数
//水果生成时执行一个缩放动作
cc.tween(fruit)
.to(0.5, { scale: 1 }, { easing: 'backOut' })
.call(() => {
this.targetFruit = fruit;//将我们控制的水果targetFruit设置为刚生成的fruit
})
.start()
}
继续新建一个InputController脚本 挂在Canvas上 ,该脚本控制水果,点击哪里水果就会移动到那个位置的坐标x,滑动时水果会跟随,松开水果下落
直接上代码
touchNum: number = 0;
// LIFE-CYCLE CALLBACKS:
// onLoad () {}
start() {
this.openTouch();
}
// update (dt) {}
openTouch() {
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
onTouchStart(e) {
if (GameManager.Instance.targetFruit != null) {//检测targetFruit是否为空 空就不执行任何动作
this.touchNum = 1;
let posx = this.node.convertToNodeSpaceAR(e.getLocation()).x;//获取点击位置的x值
let posy = GameManager.Instance.targetFruit.y;//获取上方水果的y值
cc.tween(GameManager.Instance.targetFruit)
.to(0.1, { position: cc.v3(posx, posy, 0) })
.start()
}
}
onTouchMove(e) {
if (GameManager.Instance.targetFruit != null) {
this.touchNum = 1;
GameManager.Instance.targetFruit.x = this.node.convertToNodeSpaceAR(e.getLocation()).x; //水果跟随鼠标移动
}
}
onTouchEnd(e) {
if (GameManager.Instance.targetFruit != null && this.touchNum == 1) { //this.touchNUm 是防止下落位置错误
this.touchNum = 0;
// 给targetFruit重新设置物理参数
// 碰撞器的半径为fruit高度的一半
GameManager.Instance.targetFruit.getComponent(cc.PhysicsCircleCollider).radius = GameManager.Instance.targetFruit.height / 2;
// 保存
GameManager.Instance.targetFruit.getComponent(cc.PhysicsCircleCollider).apply();
// 刚体类型改为动态
GameManager.Instance.targetFruit.getComponent(cc.RigidBody).type = cc.RigidBodyType.Dynamic;
// 给一个初始的下落线速度
GameManager.Instance.targetFruit.getComponent(cc.RigidBody).linearVelocity = cc.v2(0, -800)
// 属性改完后,targetFruit设置为空,防止连续操作
GameManager.Instance.targetFruit = null;
this.scheduleOnce(() => {
GameManager.Instance.createOneFruit(Math.floor(Math.random() * 5), cc.v2(0, 500));
}, 0.5)
}
}
运行起来:
下面就剩合成方法了,继续搞。。。好累
新建脚本FruitCollision,挂在水果预制体上
fruitNumber: number = 0;//代表自身是那种类型的水果
returnNumber: boolean = false;
getNumberTime: number = 0;
// LIFE-CYCLE CALLBACKS:
// onLoad () {}
start() {
}
update(dt) {
if (this.returnNumber) {
this.scheduleOnce(() => {
this.getNumberTime = 0;
}, 0.25)
this.returnNumber = false;
}
}
// 防止多次碰撞
getNumber() {
let ad = this.getNumberTime;
this.getNumberTime++;
this.returnNumber = true;
return ad;
}
onBeginContact(contact, selfCollider, otherCollider) {
if (otherCollider.node.group == "fruit") {
//this.endCtrl = true;
// 只有下方的水果触发碰撞回调
if (selfCollider.node.y < otherCollider.node.y) {
return
}
// 水果一下落,放在FruitNode节点下
selfCollider.node.parent = cc.find("Canvas/FruitNode");
if (selfCollider.node.getComponent(cc.RigidBody) != null) {
selfCollider.node.getComponent(cc.RigidBody).angularVelocity = 0;
// 限制一下线速度
}
let selfNum = this.fruitNumber;
let otherNum = otherCollider.node.getComponent("FruitCollision").fruitNumber;
// 水果类型相同的合成
if (selfNum == otherNum && selfNum < 9 && otherNum < 9) {
if (selfCollider.node.getComponent("FruitCollision").getNumber() == 0) {
otherCollider.node.getComponent(cc.PhysicsCircleCollider).radius = 0;
otherCollider.node.getComponent(cc.PhysicsCircleCollider).apply()
this.node.getComponent(cc.PhysicsCircleCollider).radius = 0;
this.node.getComponent(cc.PhysicsCircleCollider).apply();
cc.tween(selfCollider.node)
.to(0.1, { position: otherCollider.node.position })
.call(() => {
//生成下一个等级的水果
GameManager.Instance.score += this.fruitNumber + 1;
GameManager.Instance.createLevelUpFruit(this.fruitNumber + 1, otherCollider.node.position);
otherCollider.node.active = false;
selfCollider.node.active = false;
otherCollider.node.destroy();
selfCollider.node.destroy();
})
.start()
}
} else if (selfNum == otherNum && selfNum == 9 && otherNum == 9) {
if (selfCollider.node.getComponent("FruitCollision").getNumber() == 0) {
otherCollider.node.getComponent(cc.PhysicsCircleCollider).radius = 0;
otherCollider.node.getComponent(cc.PhysicsCircleCollider).apply()
this.node.getComponent(cc.PhysicsCircleCollider).radius = 0;
this.node.getComponent(cc.PhysicsCircleCollider).apply();
cc.tween(selfCollider.node)
.to(0.1, { position: otherCollider.node.position })
.call(() => {
GameManager.Instance.score += this.fruitNumber + 1;
GameManager.Instance.createLevelUpFruit(this.fruitNumber + 1, otherCollider.node.position);
otherCollider.node.active = false;
selfCollider.node.active = false;
otherCollider.node.destroy();
selfCollider.node.destroy();
})
.start()
}
}
}
}
再次运行,已经可以合成了,合成更高级的水果我换成了独立的方法 卸载GameManager里
/**
* 生成高一级的水果
* @param _fruitNum 水果
* @param _pos 位置
*/
createLevelUpFruit(_fruitNum, _pos) {
//AudioManager.Instance.Play(6, false, 1)
let fruit = cc.instantiate(this.fruitPre);
fruit.parent = this.fruitNode;
fruit.getComponent(cc.Sprite).spriteFrame = this.Allfruit[_fruitNum];
fruit.getComponent("FruitCollision").fruitNumber = _fruitNum;
fruit.position = _pos;
fruit.scale = 0;
fruit.getComponent(cc.RigidBody).linearVelocity = cc.v2(0, -100);
fruit.getComponent(cc.PhysicsCircleCollider).radius = fruit.height / 2;
fruit.getComponent(cc.PhysicsCircleCollider).apply();
cc.tween(fruit)
.to(0.5, { scale: 1 }, { easing: 'backOut' })
.call(() => {
if (fruit.getComponent(cc.PhysicsCircleCollider) != null) {
fruit.getComponent(cc.PhysicsCircleCollider).radius = fruit.height / 2;
fruit.getComponent(cc.RigidBody).type = cc.RigidBodyType.Dynamic;
fruit.getComponent(cc.PhysicsCircleCollider).apply();
}
})
.start()
GameManager.Instance.fruitHeigth = GameManager.Instance.findHighestFruit();
}
后面红线预警方法,结束检测方法,结束方法, 加分等,我会另外加上并打成压缩包。
可以关注我公众号“懒惰的An” 回复“大西瓜”获取代码
也可以在本公众号回复关键字获取源码:
大西瓜