Laya 关卡地图缩放
简单的样子 如上图所示
先理清思路,确定设计需求
设想一下,关卡地图有背景图,N多个关卡,如果每个关卡设计成节点,添加再关卡图上,这样拖动和缩放就比较好解决,所以整理后有3个需求
1.关卡地图拖动
2.关卡地图缩放
3.点击关卡,被点击的关卡居中显示
解决关卡地图的拖动
拖动的效果,首先是手指往哪个方向滑动,地图就往哪边移动。例如手指向上滑动,就向上移动。然后移动到一定的位置后,达到边界,不能再移动。整个逻辑和打飞机游戏,控制飞机的移动一样。背景图累加,触控点的差值。
/**
* 地图拖动
*/
private mapDrag(): void {
let mapX = this._mapPoint.x
let mapY = this._mapPoint.y
let posX = mapX + (Laya.stage.mouseX - this._lastMouseDown.x)
let posY = mapY + (Laya.stage.mouseY - this._lastMouseDown.y)
let isXMove: boolean = true
let isYMove: boolean = true
// 地图超过边界,并且移动方向和地图通向时 限制移动
if (posX < this._leftBorder && posX <= mapX) { isXMove = false }
if (posX > this._rightBorder && posX >= mapX) { isXMove = false }
if (posY < this._topBorder && posY <= mapY) { isYMove = false }
if (posY > this._bottomBorder && posY >= mapY) { isYMove = false }
if (isXMove) { this._mapPoint.x = posX }
if (isYMove) { this._mapPoint.y = posY }
this._lastMouseDown.x = Laya.stage.mouseX
this._lastMouseDown.y = Laya.stage.mouseY
this.updateImgPos()
}
/**
* 更新图片位置
*/
private updateImgPos(): void {
let point = new Laya.Point(this._mapPoint.x, this._mapPoint.y)
this.globalToLocal(point, false)
this.imgView.x = point.x
this.imgView.y = point.y
}
这里的重置图片位置,先转换成了屏幕坐标,因为触控点是stage上的位置,所以都用stage的坐标系。图片的位置是(0,0),锚点也是(0,0)。这样计算位置时,就比较方便,虽然是stage的子节点,但是起始点都是一样,在实际效果中就不会有误差
UI像这样
解决关卡地图的缩放
这个缩放参考了大佬的博客,很强大,感谢大佬的分享
https://blog.****.net/winnershili/article/details/80093017
http://bl.ocks.org/sgruhier/1d692762f8328a2c9957
主要实现了以鼠标点为中心,控制缩放。通过调整位置 和缩放值,达到聚焦缩放的效果。然后在改成触摸屏幕缩放时,发现如果第一次触摸,就是刚把手指放上去时,就调整了位置,这样实际效果看起来不是很平滑。还有就是缩放后,关卡地图的大小变了,这样边界也需要调一下
1.初次触摸,不改变
2.调整边界的位置
/**
* 鼠标移动
*/
private onMouseMove(e: Laya.Event): void {
if (!this._isMouseDown) { return }
if (e.touches && e.touches.length >= 2) {
this.touchScaleMap(e.touches)
return
}
this.mapDrag()
}
/**
* 鼠标抬起
*/
private onMouseUp(e: Laya.Event): void {
this._isMouseDown = false
this._lastDistance = 0
}
/**
* 鼠标按下
*/
private onMouseDown(): void {
this._isMouseDown = true
this._lastMouseDown.x = Laya.stage.mouseX
this._lastMouseDown.y = Laya.stage.mouseY
}
/**
* 触控缩放
*/
private touchScaleMap(points: Array<any>): void {
//计算中心点
this._scaleCenter.x = (points[0].stageX + points[1].stageX) * 0.5
this._scaleCenter.y = (points[0].stageY + points[1].stageY) * 0.5
//计算缩放值
let distance: number = this.getDistance(points);
let changeValue = (distance - this._lastDistance) * this._scaleSmoothness
let isFirst = false
if (this._lastDistance == 0) { isFirst = true }
this._lastDistance = distance
if (isFirst) { return }
this.changeScale(changeValue)
}
/**
* 改变缩放值
*/
private changeScale(changeValue): void {
this._currentScale += changeValue
if (this._currentScale > 2) {
this._currentScale = 2
}
if (this._currentScale < 0.3) {
this._currentScale = 0.3
}
this.mapScale()
}
/**
* 地图缩放
*/
private mapScale(): void {
let mapX = this._mapPoint.x
let mapY = this._mapPoint.y
let disX = this._scaleCenter.x - mapX
let disY = this._scaleCenter.y - mapY
let scareRateDis = (this._currentScale - this._lastScale) / this._lastScale
this._mapPoint.x = mapX - disX * scareRateDis
this._mapPoint.y = mapY - disY * scareRateDis
this._lastScale = this._currentScale
this.updateImgPos()
this.updateImageScale()
this.setBorder()
}
这里自适应为了在手机中图片看起来不花,设置的浏览器宽高,自定义调整缩放,如果不是这样的,UIUtil.scale不要就可以了
/**
* 构造函数
*/
constructor() {
//TS或JS版本初始化微信小游戏的适配
Laya.MiniAdpter.init();
Laya.MiniAdpter['getUrlEncode'] = Main.getUrlAndEncode;
Laya.init(Laya.Browser.width, Laya.Browser.height, laya.webgl.WebGL);
Laya.stage.frameRate = Laya.Stage.FRAME_FAST;
Laya.stage.scaleMode = Laya.Stage.SCALE_NOSCALE;
UIConfig.closeDialogOnSide = false;
Main.startLoad()
}
/**
* 设置边界
*/
private setBorder(): void {
this.boxMask.visible = false
let borderDis = 100 * UIUtil.scale
// 图片在手机中的宽高 自适应+缩放
let imgWidthInPhone = this.imgView.width * UIUtil.scale * this._currentScale
let imgHeightInPhone = this.imgView.height * UIUtil.scale * this._currentScale
// 屏幕的真是宽高
let screenWidth = Laya.Browser.width
let screenHeight = Laya.Browser.height
//设置边界
this._leftBorder = -imgWidthInPhone - borderDis + screenWidth
this._rightBorder = borderDis
this._topBorder = -imgHeightInPhone - borderDis + screenHeight
this._bottomBorder = borderDis
}
解决点击关卡,关卡位置居中
想法是,计算关卡位置 和屏幕中心位置 之间的差值,然后用tween 动画移动,为什么用tween动画,是为了让它平滑移动,看起来不会突然。然后这个关卡是添加在背景图上的,所以要转换成stage坐标。因为用来自定义缩放做手机适配,这里直接用localToGlobal转换的坐标,有误差,本来不会有的。自定义缩放适配,是在添加到stage的时候,根据屏幕高宽和设计时的高宽比,调整缩放,那么它的字节点坐标,和直接添加在stage上比,就是被缩放过的。所以这里自己计算一下,先获得和父节点同一个parent的坐标,,关卡的坐标*缩放值+父节点坐标=在父节点parent的坐标,把这个坐标转成世界坐标。
1.平滑过度
2.计算关卡位置和屏幕中心的位置
3.tween动画时,不能触摸
/**
* 关卡节点点击
*/
private onLevelNodeClick(point: Laya.Point): void {
point.x *= this._currentScale
point.y *= this._currentScale
point.x += this.imgView.x
point.y += this.imgView.y
this.localToGlobal(point, false)
let mapX = this._mapPoint.x
let mapY = this._mapPoint.y
this._lastMouseDown.x = point.x
this._lastMouseDown.y = point.y
let posX = mapX + (Laya.stage.width * 0.5 - this._lastMouseDown.x)
let posY = mapY + (Laya.stage.height * 0.5 - this._lastMouseDown.y)
this._lastMouseDown.x = point.x
this._lastMouseDown.y = point.y
let ease = Laya.Ease.linearNone
let updateFunc: Laya.Handler = new Laya.Handler(this, this.updateImgPos)
this.boxMask.visible = true
let compeleteFunc: Laya.Handler = Laya.Handler.create(this, this.onLevelNodeClickOver)
Laya.Tween.to(this._mapPoint, { x: posX, y: posY, update: updateFunc }, 300, ease, compeleteFunc, 0, true, true)
}
在UI上添加的boxMask就是为了不让触摸。下面是自定义适配
/**
* 添加到舞台
* @param ui
*/
public static addToStage(ui: Laya.Sprite): void {
if (null == ui) {
return;
}
if (0 == this._scale) {
this._scale = Laya.Browser.width / GameConfig.width;
}
ui.scale(this._scale, this._scale);
Laya.stage.addChild(ui);
}
/**
* 缩放比例
*/
public static get scale(): number {
if (0 == this._scale) {
return Laya.Browser.width / GameConfig.width;
}
return this._scale;
}