372 lines
12 KiB
JavaScript
372 lines
12 KiB
JavaScript
//=============================================================================
|
||
// RPG Maker MZ - Sprite_Scrollable
|
||
//
|
||
// Copyright 2024 Panzer-IV. All rights reserved.
|
||
// This source code or any portion thereof must not be
|
||
// reproduced or used without licensed in any manner whatsoever.
|
||
//=============================================================================
|
||
/*:
|
||
* @target MZ
|
||
* @plugindesc スクロール対応スプライト
|
||
* @author 四号戦車
|
||
* @base DebugSwitch
|
||
* @base Sprite_InputHandle
|
||
* @orderAfter DebugSwitch
|
||
* @orderAfter Sprite_InputHandle
|
||
*
|
||
* @param comment
|
||
* @text 備考欄
|
||
* @desc プラグイン側では使用しません。
|
||
* メモ欄としてお使いください。
|
||
* @type multiline_string
|
||
* @default
|
||
*
|
||
* @help Sprite_Scrollable.js
|
||
*
|
||
* Version: 0.0.1
|
||
*
|
||
* 内部のDisplayObjectの展開領域に関わらず固定の表示領域にクロップするSpriteです。
|
||
*/
|
||
(() => {
|
||
"use strict";
|
||
|
||
//========================================================================
|
||
// プラグイン定義
|
||
//========================================================================
|
||
const pluginName = "Sprite_Scrollable";
|
||
const script = document.currentScript;
|
||
const param = PluginManagerEx.createParameter(script);
|
||
|
||
//========================================================================
|
||
// 定数定義
|
||
//========================================================================
|
||
|
||
//========================================================================
|
||
// コアスクリプト変更部 (Sprite)
|
||
//========================================================================
|
||
|
||
//========================================================================
|
||
// クラス定義 (Sprite_Scrollable)
|
||
//========================================================================
|
||
class Sprite_Scrollable extends Sprite_InputHandle {
|
||
constructor(width, height, mask) {
|
||
super();
|
||
this.reservedMask = mask;
|
||
this.container = new Sprite();
|
||
this.container.zIndex = 10;
|
||
this.addChildToRoot(this.container);
|
||
this.innerRect = null;
|
||
this.overallRect = this.container.getBounds();
|
||
this.mergin = null;
|
||
this.setMergin(0, 0, 0, 0);
|
||
this.setFrame(0, 0, width, height);
|
||
|
||
this._enableInput = true;
|
||
this._enableScroll = true;
|
||
this._enableScrollX = true;
|
||
this._enableScrollY = true;
|
||
|
||
this._limitInBounds = true;
|
||
|
||
this._scrollX = 0;
|
||
this._scrollY = 0;
|
||
this.clearScrollStatus();
|
||
}
|
||
|
||
clearScrollStatus() {
|
||
this._scrollTargetX = 0;
|
||
this._scrollTargetY = 0;
|
||
this._scrollDuration = 0;
|
||
this._scrollAccelX = 0;
|
||
this._scrollAccelY = 0;
|
||
this._scrollTouching = false;
|
||
this._scrollLastTouchX = 0;
|
||
this._scrollLastTouchY = 0;
|
||
}
|
||
|
||
setFrame(x, y, width, height) {
|
||
super.setFrame(x, y, width, height);
|
||
this.innerRect = new Rectangle(x, y, width, height);
|
||
|
||
let mask = this.reservedMask;
|
||
if (!mask) {
|
||
mask = new Sprite(new Bitmap(width, height));
|
||
mask.bitmap.fillAll('#FFFFFF');
|
||
}
|
||
|
||
if (!!this.mask) {
|
||
this.removeChild(this.mask);
|
||
}
|
||
super.addChild(mask);
|
||
this.mask = mask;
|
||
}
|
||
|
||
getBounds(skipUpdate, rect) {
|
||
// transformの更新のため呼び出し(結果は捨てる)
|
||
this.container.getBounds();
|
||
const b = super.getBounds(skipUpdate);
|
||
const bounds = !!this.innerRect ? this.innerRect : new Rectangle(this.x, this.y, b.width, b.height);
|
||
|
||
bounds.x = this.x;
|
||
bounds.y = this.y;
|
||
|
||
if (!!rect) {
|
||
rect.x = bounds.x;
|
||
rect.y = bounds.y;
|
||
rect.width = bounds.width;
|
||
rect.height = bounds.height;
|
||
return rect;
|
||
} else {
|
||
return bounds;
|
||
}
|
||
}
|
||
|
||
enableInput(enable) {
|
||
this._enableInput = enable;
|
||
}
|
||
|
||
enableScroll(enable) {
|
||
this._enableScroll = enable;
|
||
}
|
||
|
||
enableScrollX(enable) {
|
||
this._enableScrollX = enable;
|
||
}
|
||
|
||
enableScrollY(enable) {
|
||
this._enableScrollY = enable;
|
||
}
|
||
|
||
enableLimit(enable) {
|
||
this._limitInBounds = enable;
|
||
}
|
||
|
||
setMergin(left, top, right, bottom) {
|
||
this.mergin = {
|
||
top: top,
|
||
bottom: bottom,
|
||
left: left,
|
||
right: right,
|
||
};
|
||
}
|
||
|
||
scrollX() {
|
||
return this._scrollX;
|
||
}
|
||
|
||
scrollY() {
|
||
return this._scrollY;
|
||
}
|
||
|
||
scrollTo(x, y) {
|
||
const scrollX = this._limitInBounds ? x.clamp(-this.mergin.left, this.maxScrollX()) : x;
|
||
const scrollY = this._limitInBounds ? y.clamp(-this.mergin.top, this.maxScrollY()) : y;
|
||
if (this._scrollX !== scrollX || this._scrollY !== scrollY) {
|
||
if (this._enableScrollX) {
|
||
this._scrollX = scrollX;
|
||
}
|
||
|
||
if (this._enableScrollY) {
|
||
this._scrollY = scrollY;
|
||
}
|
||
this.updateOrigin();
|
||
}
|
||
}
|
||
|
||
scrollBy(x, y) {
|
||
this.scrollTo(this._scrollX + x, this._scrollY + y);
|
||
}
|
||
|
||
smoothScrollTo(x, y, duration) {
|
||
this._scrollTargetX = this._limitInBounds ? x.clamp(0, this.maxScrollX()) : x;
|
||
this._scrollTargetY = this._limitInBounds ? y.clamp(0, this.maxScrollY()) : y;
|
||
this._scrollDuration = duration || Input.keyRepeatInterval;
|
||
}
|
||
|
||
smoothScrollBy(x, y, duration) {
|
||
if (this._scrollDuration === 0) {
|
||
this._scrollTargetX = this.scrollX();
|
||
this._scrollTargetY = this.scrollY();
|
||
}
|
||
this.smoothScrollTo(this._scrollTargetX + x, this._scrollTargetY + y, duration);
|
||
}
|
||
|
||
setScrollAccel(x, y) {
|
||
this._scrollAccelX = x;
|
||
this._scrollAccelY = y;
|
||
}
|
||
|
||
overallWidth() {
|
||
return this.overallRect.width + this.mergin.left + this.mergin.right;
|
||
}
|
||
|
||
overallHeight() {
|
||
return this.overallRect.height + this.mergin.top + this.mergin.bottom;
|
||
}
|
||
|
||
maxScrollX() {
|
||
return Math.max(0, this.overallWidth() - this.innerRect.width);
|
||
}
|
||
|
||
maxScrollY() {
|
||
return Math.max(0, this.overallHeight() - this.innerRect.height);
|
||
}
|
||
|
||
update() {
|
||
super.update();
|
||
this.overallRect = this.container.getBounds();
|
||
|
||
if (!this._enableScroll) {
|
||
return;
|
||
}
|
||
|
||
if (this._enableInput) {
|
||
this.processWheelScroll();
|
||
this.processTouchScroll();
|
||
}
|
||
this.updateSmoothScroll();
|
||
this.updateScrollAccel();
|
||
this.updateOrigin();
|
||
}
|
||
|
||
processWheelScroll() {
|
||
if (this.isWheelScrollEnabled() && this.isTouchedInsideFrame()) {
|
||
const threshold = 20;
|
||
if (TouchInput.wheelY >= threshold || TouchInput.wheelY <= -threshold) {
|
||
this.smoothScrollBy(0, TouchInput.wheelY);
|
||
}
|
||
}
|
||
}
|
||
|
||
processTouchScroll() {
|
||
if (this.isTouchScrollEnabled()) {
|
||
if (TouchInput.isTriggered() && this.isTouchedInsideFrame()) {
|
||
this.onTouchScrollStart();
|
||
}
|
||
if (this._scrollTouching) {
|
||
if (TouchInput.isReleased()) {
|
||
this.onTouchScrollEnd();
|
||
} else if (TouchInput.isMoved()) {
|
||
this.onTouchScroll();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
isWheelScrollEnabled() {
|
||
return this.isScrollEnabled();
|
||
}
|
||
|
||
isTouchScrollEnabled() {
|
||
return this.isScrollEnabled();
|
||
}
|
||
|
||
isScrollEnabled() {
|
||
return true;
|
||
}
|
||
|
||
isTouchedInsideFrame() {
|
||
// transformの更新(結果は捨てる)
|
||
this.getBounds();
|
||
|
||
// スクロール量の補正をかける
|
||
const touchPos = new Point(TouchInput.x, TouchInput.y + this.container.y);
|
||
const localPos = this.worldTransform.applyInverse(touchPos);
|
||
|
||
const rect = super.getBounds(false);
|
||
const start = this.worldTransform.applyInverse(new Point(rect.x, rect.y));
|
||
const frame = new Rectangle(start.x, start.y, this.innerRect.width, this.innerRect.height);
|
||
return frame.contains(localPos.x, localPos.y);
|
||
}
|
||
|
||
onTouchScrollStart() {
|
||
this._scrollTouching = true;
|
||
this._scrollLastTouchX = TouchInput.x;
|
||
this._scrollLastTouchY = TouchInput.y;
|
||
this.setScrollAccel(0, 0);
|
||
}
|
||
|
||
onTouchScroll() {
|
||
let accelX = this._scrollLastTouchX - TouchInput.x;
|
||
if (this.overallWidth() <= this.width) {
|
||
accelX = 0;
|
||
}
|
||
let accelY = this._scrollLastTouchY - TouchInput.y;
|
||
if (this.overallHeight() <= this.height) {
|
||
accelY = 0;
|
||
}
|
||
this.setScrollAccel(-accelX, accelY);
|
||
this._scrollLastTouchX = TouchInput.x;
|
||
this._scrollLastTouchY = TouchInput.y;
|
||
}
|
||
|
||
onTouchScrollEnd() {
|
||
this._scrollTouching = false;
|
||
}
|
||
|
||
updateSmoothScroll() {
|
||
if (this._scrollDuration > 0) {
|
||
const d = this._scrollDuration;
|
||
const deltaX = (this._scrollTargetX - this._scrollX) / d;
|
||
const deltaY = (this._scrollTargetY - this._scrollY) / d;
|
||
this.scrollBy(deltaX, deltaY);
|
||
this._scrollDuration--;
|
||
}
|
||
}
|
||
|
||
updateScrollAccel() {
|
||
if (this._scrollAccelX !== 0 || this._scrollAccelY !== 0) {
|
||
this.scrollBy(this._scrollAccelX, this._scrollAccelY);
|
||
this._scrollAccelX *= 0.92;
|
||
this._scrollAccelY *= 0.92;
|
||
if (Math.abs(this._scrollAccelX) < 1) {
|
||
this._scrollAccelX = 0;
|
||
}
|
||
if (Math.abs(this._scrollAccelY) < 1) {
|
||
this._scrollAccelY = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
updateOrigin() {
|
||
this.container.x = this._scrollX;
|
||
this.container.y = -this._scrollY;
|
||
}
|
||
|
||
addChild(child) {
|
||
this.container.addChild(child);
|
||
}
|
||
|
||
addChildAt(child, index) {
|
||
this.container.addChildAt(child, index);
|
||
}
|
||
|
||
removeChild(child) {
|
||
return this.container.removeChild(child);
|
||
}
|
||
|
||
removeChildAt(index) {
|
||
return this.container.removeChildAt(index);
|
||
}
|
||
|
||
removeChildren(beginIndex, endIndex) {
|
||
return this.container.removeChildren(beginIndex, endIndex);
|
||
}
|
||
|
||
swapChildren(child, child2) {
|
||
return this.container.swapChildren(child, child2);
|
||
}
|
||
|
||
calculateBounds() {
|
||
super.calculateBounds();
|
||
this.overallRect = this.container.getBounds();
|
||
}
|
||
|
||
addChildToRoot(child) {
|
||
super.addChild(child);
|
||
super.sortChildren();
|
||
}
|
||
}
|
||
window.Sprite_Scrollable = Sprite_Scrollable;
|
||
|
||
})();
|