//============================================================================= // 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; })();