最近在写一个帧同步的小游戏, 服务器用的pinus, 客户端就是cocos creator了
因为要实现玩家在物理环境的跳跃, 碰撞, 移动, 最开始准备使用cocos creator的物理引擎, 但是实际操作是发现并不可行
我的服务器接收和转发客户端的消息, 玩家移动位置使用过方向命令实现的, 并不是直接发位置信息, 这就导致由于客户端的差异而造成位置出现差异, 如此, 需要自己搭建一个物理环境
我们为角色设置一些参数
speed: cc.v2(0, 0), // 角色移动的速度
maxSpeed: cc.v2(2000, 2000), // 角色移动的最大速度
gravity: -1000, // 角色受到的重力
drag: 1000,
direction: 0, // 方向
jumpSpeed: 300 // 跳跃速度
通过gravity 我们可以模拟角色受到重力的效果
在update中
this.speed.y += this.gravity * dt;
if (Math.abs(this.speed.y) > this.maxSpeed.y) {
this.speed.y = this.speed.y > 0 ? this.maxSpeed.y : -this.maxSpeed.y;
这样 角色就会一直受到重力的影响向下运动
我们在做一个地面, 让角色可以站在上面
大概这样, 记得要添加碰撞组件 , 然后分组, 当然也要开启物理引擎
在角色上添加好碰撞回调
onCollisionEnter(other, self);
onCollisionStay(other, self);
onCollisionExit(other, self);
那么重点来了, 就是如何让玩家站在地面上
代码如下, 讲解一下
首先添加属性 collisionX 表示横向碰撞, 即左右碰撞, -1表示左边发送碰撞, 1表示右边发生碰撞
collisionY 同理
var otherAabb = other.world.aabb;
var otherPreAabb = other.world.preAabb.clone();
var selfAabb = self.world.aabb;
var selfPreAabb = self.world.preAabb.clone();
获取自己和对方的包围盒, 以及上一帧的包围盒, 为什么要回去上一帧的包围盒呢, 因为当前帧以及发送碰撞, 即角色卡在地面了(有部分重合), 而上一帧就是角色站在地面上的感觉
还有一个点, 包围盒是世界坐标下的, 和触摸事件一样, 基于世界坐标
else if (this.speed.x > 0 && (selfPreAabb.xMin < otherPreAabb.xMin)) {
this.node.x = otherPreAabb.xMin - selfPreAabb.width - this.node.parent.x;
this.collisionX = 1;
}
this.speed.x = 0;
other.touchingX = true;
return;
}
首先判断是否相撞, 加粗的地方是关键, this.speed.x < 0 表示玩家是向左移动时发生的碰撞 在判断自己的包围盒右边是否在对方包围盒的右边, 如下图 即4是否在2右边,
如果判断语句正确, 碰撞发生了
这个时候, 设置玩家的位置 就是把黑方框, 放到红方框右边靠着, 靠多少可以自己调整
!!!重点 this.node.parent.x 必须是基于世界坐标的, 因为我这里刚好与世界坐标一样
这是我另一个项目中的
this.node.x = this.node.parent.convertToNodeSpaceAR(cc.v2(otherPreAabb.xMax+selfPreAabb.width/2, 0)).x;
this.node.color = cc.Color.RED;
this.touchingNumber ++;
// 1st step
// get pre aabb, go back before collision
var otherAabb = other.world.aabb;
var otherPreAabb = other.world.preAabb.clone();
var selfAabb = self.world.aabb;
var selfPreAabb = self.world.preAabb.clone();
// 2nd step
// forward x-axis, check whether collision on x-axis
selfPreAabb.x = selfAabb.x;
otherPreAabb.x = otherAabb.x;
if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {
if (this.speed.x < 0 && (selfPreAabb.xMax > otherPreAabb.xMax)) {
this.node.x = otherPreAabb.xMax - this.node.parent.x;
this.collisionX = -1;
}
else if (this.speed.x > 0 && (selfPreAabb.xMin < otherPreAabb.xMin)) {
this.node.x = otherPreAabb.xMin - selfPreAabb.width - this.node.parent.x;
this.collisionX = 1;
}
this.speed.x = 0;
other.touchingX = true;
return;
}
// 3rd step
// forward y-axis, check whether collision on y-axis
selfPreAabb.y = selfAabb.y;
otherPreAabb.y = otherAabb.y;
if (cc.Intersection.rectRect(selfPreAabb, otherPreAabb)) {
if (this.speed.y < 0 && (selfPreAabb.yMax > otherPreAabb.yMax)) {
this.node.y = otherPreAabb.yMax - this.node.parent.y;
this.jumping = false;
this.collisionY = -1;
}
else if (this.speed.y > 0 && (selfPreAabb.yMin < otherPreAabb.yMin)) {
this.node.y = otherPreAabb.yMin - selfPreAabb.height - this.node.parent.y;
this.collisionY = 1;
}
this.speed.y = 0;
other.touchingY = true;
}
update(dt)
if (this.collisionY === 0) {
this.speed.y += this.gravity * dt;
if (Math.abs(this.speed.y) > this.maxSpeed.y) {
this.speed.y = this.speed.y > 0 ? this.maxSpeed.y : -this.maxSpeed.y;
}
}
if (this.direction === 0) {
if (this.speed.x > 0) {
this.speed.x -= this.drag * dt;
if (this.speed.x <= 0) this.speed.x = 0;
}
else if (this.speed.x < 0) {
this.speed.x += this.drag * dt;
if (this.speed.x >= 0) this.speed.x = 0;
}
}
else {
this.speed.x += (this.direction > 0 ? 1 : -1) * this.drag * dt;
if (Math.abs(this.speed.x) > this.maxSpeed.x) {
this.speed.x = this.speed.x > 0 ? this.maxSpeed.x : -this.maxSpeed.x;
}
}
if (this.speed.x * this.collisionX > 0) {
this.speed.x = 0;
}
this.prePosition.x = this.node.x;
this.prePosition.y = this.node.y;
this.preStep.x = this.speed.x * dt;
this.preStep.y = this.speed.y * dt;
this.node.x += this.speed.x * dt;
this.node.y += this.speed.y * dt;
},