@maoyugames/phaser-framework 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -620,7 +620,7 @@ declare abstract class BasePanel extends Phaser.GameObjects.Container {
620
620
  color?: number;
621
621
  alpha?: number;
622
622
  closeOnClick?: boolean;
623
- }): Phaser.GameObjects.Graphics;
623
+ }): Phaser.GameObjects.Image;
624
624
  /**
625
625
  * 把一个子对象放到设计稿正中(以设计稿坐标系)。
626
626
  * 仅设置坐标,不负责 add(由调用方决定何时 add 进容器)。
@@ -1057,6 +1057,13 @@ interface ButtonConfig extends ButtonStyle {
1057
1057
  declare class Button extends Phaser.GameObjects.Container {
1058
1058
  private readonly bg;
1059
1059
  private readonly label;
1060
+ /**
1061
+ * 透明输入命中层(Image,使用默认 frame 命中区)。
1062
+ * 不直接在容器上 setInteractive(自定义 Rectangle) 的原因:真机/高分屏 Phaser FIT
1063
+ * 非整数缩放时,容器自定义矩形命中区会随离中心距离产生偏移,小按钮点不准;
1064
+ * Image 的 frame 命中区不受此影响。故输入统一走这一层。
1065
+ */
1066
+ private readonly hit;
1060
1067
  private readonly style;
1061
1068
  private readonly onClick?;
1062
1069
  /** 是否禁用 */
@@ -1071,7 +1078,58 @@ declare class Button extends Phaser.GameObjects.Container {
1071
1078
  get disabled(): boolean;
1072
1079
  /** 绘制圆角背景(以中心为原点) */
1073
1080
  private drawBackground;
1074
- /** 绑定指针交互:悬停/按下/抬起,实现按下态与点击触发 */
1081
+ /** 绑定指针交互:悬停/按下/抬起,实现按下态与点击触发(事件挂在命中层 hit 上) */
1082
+ private bindEvents;
1083
+ }
1084
+
1085
+ /**
1086
+ * 圆形图标按钮 IconButton(基于 Phaser.GameObjects.Container)。
1087
+ *
1088
+ * 与 Button 的区别:Button 是「圆角矩形 + 文字」,IconButton 是「圆底 + 图标纹理」,
1089
+ * 适合返回/设置/帮助等小图标按钮。
1090
+ *
1091
+ * 输入用透明 Image 命中层(默认 frame 命中区)+ down/up 触发——在真机/高分屏
1092
+ * (Phaser FIT 非整数缩放)下命中可靠,规避「容器自定义 Rectangle 命中区小按钮点不准」的坑。
1093
+ *
1094
+ * 用法:
1095
+ * new IconButton(this, { x, y, size: 88, texture: 'icon_home', onClick: () => goMain() });
1096
+ */
1097
+
1098
+ interface IconButtonConfig {
1099
+ x?: number;
1100
+ y?: number;
1101
+ /** 直径(圆底与命中区尺寸),默认 88 */
1102
+ size?: number;
1103
+ /** 图标纹理 key */
1104
+ texture: string;
1105
+ /** 图标帧(图集时用,可选) */
1106
+ frame?: string | number;
1107
+ /** 图标相对 size 的比例,默认 0.56 */
1108
+ iconScale?: number;
1109
+ /** 圆底颜色;传 null 不绘制底,默认深灰 0x232c44 */
1110
+ bgColor?: number | null;
1111
+ /** 圆底不透明度,默认 1 */
1112
+ bgAlpha?: number;
1113
+ /** 水平翻转图标(如用「下一关」箭头翻成「上一关」) */
1114
+ flipX?: boolean;
1115
+ /** 图标着色 */
1116
+ iconTint?: number;
1117
+ /** 点击回调(按下并在按钮内抬起才触发) */
1118
+ onClick?: () => void;
1119
+ }
1120
+ declare class IconButton extends Phaser.GameObjects.Container {
1121
+ private readonly icon;
1122
+ /** 透明命中层(frame 命中区,真机/高分屏可靠) */
1123
+ private readonly hit;
1124
+ private readonly onClick?;
1125
+ private _disabled;
1126
+ private pressed;
1127
+ constructor(scene: Phaser.Scene, config: IconButtonConfig);
1128
+ /** 更新图标纹理 */
1129
+ setIcon(texture: string, frame?: string | number): this;
1130
+ /** 设置禁用态 */
1131
+ setDisabled(disabled: boolean): this;
1132
+ get disabled(): boolean;
1075
1133
  private bindEvents;
1076
1134
  }
1077
1135
 
@@ -1777,4 +1835,4 @@ interface PlatformConfig {
1777
1835
  capacitor?: CapacitorPlatformConfig;
1778
1836
  }
1779
1837
 
1780
- export { AdResult, type AdsConfig, App, type AppConfig, BaseModule, BasePanel, BaseScene, Button, type ButtonConfig, type ButtonStyle, type CapacitorPlatformConfig, ConfigTable, ConfigTableManager, ErrorCode, type FacebookPlatformConfig, FrameworkError, Handler, HttpClient, type HttpClientConfig, HttpError, type HttpInterceptors, HttpRequestOptions, I18n, type I18nOptions, type IAccountService, type IAdsManager, type IAnalyticsManager, type IApp, type IAssetLoader, type IAudioManager, type IConfigService, type IEventBus, type IHttpClient, type IKcpTransport, type ILogger, type INetManager, type IPanelManager, type IPaymentManager, IPlatform, IPlatformLifecycle, type ISceneManager, type ISocketClient, type IStorageManager, JsonCodec, KcpClient, type KcpClientConfig, type KcpTransportFactory, type LayerName, Layers, LogLevel, LoginResult, MessageChannel, type MessageChannelOptions, type MessageCodec, type MessageEnvelope, MessageError, ModuleRegistry, NetManager, type NetManagerOptions, type NetProtocol, ObjectPool, type Orientation, type PanelCtor, PanelManager, type PlatformConfig, PlatformContext, PurchaseResult, SafeArea, SaveManager, type SaveSlot, type SaveSlotOptions, type ScheduleHandle, Scheduler, type SocketClientConfig, type SocketState, type StartGameOptions, type Store, type TikTokPlatformConfig, TypedEventBus, Unsubscribe, type WeChatPlatformConfig, type WebPlatformConfig, WebSocketClient, createStore, createTypedEventBus, isFrameworkError, normalizeLocale, startGame };
1838
+ export { AdResult, type AdsConfig, App, type AppConfig, BaseModule, BasePanel, BaseScene, Button, type ButtonConfig, type ButtonStyle, type CapacitorPlatformConfig, ConfigTable, ConfigTableManager, ErrorCode, type FacebookPlatformConfig, FrameworkError, Handler, HttpClient, type HttpClientConfig, HttpError, type HttpInterceptors, HttpRequestOptions, I18n, type I18nOptions, type IAccountService, type IAdsManager, type IAnalyticsManager, type IApp, type IAssetLoader, type IAudioManager, type IConfigService, type IEventBus, type IHttpClient, type IKcpTransport, type ILogger, type INetManager, type IPanelManager, type IPaymentManager, IPlatform, IPlatformLifecycle, type ISceneManager, type ISocketClient, type IStorageManager, IconButton, type IconButtonConfig, JsonCodec, KcpClient, type KcpClientConfig, type KcpTransportFactory, type LayerName, Layers, LogLevel, LoginResult, MessageChannel, type MessageChannelOptions, type MessageCodec, type MessageEnvelope, MessageError, ModuleRegistry, NetManager, type NetManagerOptions, type NetProtocol, ObjectPool, type Orientation, type PanelCtor, PanelManager, type PlatformConfig, PlatformContext, PurchaseResult, SafeArea, SaveManager, type SaveSlot, type SaveSlotOptions, type ScheduleHandle, Scheduler, type SocketClientConfig, type SocketState, type StartGameOptions, type Store, type TikTokPlatformConfig, TypedEventBus, Unsubscribe, type WeChatPlatformConfig, type WebPlatformConfig, WebSocketClient, createStore, createTypedEventBus, isFrameworkError, normalizeLocale, startGame };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PlatformUnsupportedError } from './chunk-II3JM4R3.js';
2
2
  export { PlatformUnsupportedError } from './chunk-II3JM4R3.js';
3
3
  import { __publicField } from './chunk-PKBMQBKP.js';
4
- import Phaser4 from 'phaser';
4
+ import Phaser2 from 'phaser';
5
5
 
6
6
  // src/App.ts
7
7
  var parts = null;
@@ -2598,7 +2598,7 @@ var ModuleRegistry = class {
2598
2598
  return this.modules.size;
2599
2599
  }
2600
2600
  };
2601
- var BasePanel = class extends Phaser4.GameObjects.Container {
2601
+ var BasePanel = class extends Phaser2.GameObjects.Container {
2602
2602
  constructor(scene) {
2603
2603
  super(scene, 0, 0);
2604
2604
  /** 打开本面板时所属的 key(由 PanelManager 在创建后注入,用于自我关闭) */
@@ -2683,12 +2683,11 @@ var BasePanel = class extends Phaser4.GameObjects.Container {
2683
2683
  const h = this.designHeight;
2684
2684
  const color = opts?.color ?? 0;
2685
2685
  const alpha = opts?.alpha ?? 0.6;
2686
- const dim = this.scene.make.graphics({}, false);
2687
- dim.fillStyle(color, alpha);
2688
- dim.fillRect(0, 0, w, h);
2686
+ const dim = this.scene.make.image({ x: w / 2, y: h / 2, key: "__WHITE" }, false);
2687
+ dim.setDisplaySize(w, h).setTint(color).setAlpha(alpha);
2689
2688
  if (opts?.closeOnClick) {
2690
- dim.setInteractive(new Phaser4.Geom.Rectangle(0, 0, w, h), Phaser4.Geom.Rectangle.Contains);
2691
- dim.on(Phaser4.Input.Events.POINTER_UP, () => this.close());
2689
+ dim.setInteractive();
2690
+ dim.on(Phaser2.Input.Events.POINTER_UP, () => this.close());
2692
2691
  }
2693
2692
  this.addAt(dim, 0);
2694
2693
  return dim;
@@ -2947,7 +2946,7 @@ async function startGame(opts) {
2947
2946
  }
2948
2947
  function createPhaserGameInstance(config, scenes) {
2949
2948
  const gameConfig = {
2950
- type: Phaser4.AUTO,
2949
+ type: Phaser2.AUTO,
2951
2950
  // 关键:把 canvas 挂进各平台外壳的 #game-root(外壳已为它写好布局/居中 CSS)。
2952
2951
  // 不设 parent 时 Phaser 把 canvas 追加到 document.body —— 而外壳里 #game-root 占满
2953
2952
  // 100% 高度,canvas 作为其后的块级兄弟会被挤到视口外,再被 body 的 overflow:hidden
@@ -2958,19 +2957,22 @@ function createPhaserGameInstance(config, scenes) {
2958
2957
  backgroundColor: "#1f2937",
2959
2958
  scale: {
2960
2959
  // FIT:按设计分辨率等比缩放铺满容器(保持比例,留黑边);CENTER_BOTH:居中
2961
- mode: Phaser4.Scale.FIT,
2962
- autoCenter: Phaser4.Scale.CENTER_BOTH,
2960
+ mode: Phaser2.Scale.FIT,
2961
+ autoCenter: Phaser2.Scale.CENTER_BOTH,
2963
2962
  width: config.designWidth,
2963
+ // autoRound:把缩放后画布尺寸取整,避免非整数缩放(如真机 1.866x)产生亚像素偏移,
2964
+ // 渲染更清晰、指针命中更准(配合 UI 组件用 frame 命中区,彻底解决高分屏小按钮点不准)。
2965
+ autoRound: true,
2964
2966
  height: config.designHeight
2965
2967
  },
2966
2968
  scene: scenes
2967
2969
  // 微信等无 DOM 平台由 weapp-adapter 提供 canvas;有 DOM 平台 Phaser 自动建 canvas
2968
2970
  };
2969
- return new Phaser4.Game(gameConfig);
2971
+ return new Phaser2.Game(gameConfig);
2970
2972
  }
2971
2973
  function waitForGameReady(game) {
2972
2974
  return new Promise((resolve) => {
2973
- game.events.once(Phaser4.Core.Events.READY, () => resolve());
2975
+ game.events.once(Phaser2.Core.Events.READY, () => resolve());
2974
2976
  });
2975
2977
  }
2976
2978
 
@@ -3076,7 +3078,7 @@ var Scheduler = class {
3076
3078
  };
3077
3079
 
3078
3080
  // src/scene/BaseScene.ts
3079
- var BaseScene = class extends Phaser4.Scene {
3081
+ var BaseScene = class extends Phaser2.Scene {
3080
3082
  constructor(config) {
3081
3083
  super(config);
3082
3084
  /* --------------------------- 自动清理 / 调度 --------------------------- */
@@ -3126,10 +3128,10 @@ var BaseScene = class extends Phaser4.Scene {
3126
3128
  this._cleanupArmed = true;
3127
3129
  const onShutdown = () => {
3128
3130
  this.runCleanup();
3129
- this.events.once(Phaser4.Scenes.Events.SHUTDOWN, onShutdown);
3131
+ this.events.once(Phaser2.Scenes.Events.SHUTDOWN, onShutdown);
3130
3132
  };
3131
- this.events.once(Phaser4.Scenes.Events.SHUTDOWN, onShutdown);
3132
- this.events.once(Phaser4.Scenes.Events.DESTROY, () => this.runCleanup());
3133
+ this.events.once(Phaser2.Scenes.Events.SHUTDOWN, onShutdown);
3134
+ this.events.once(Phaser2.Scenes.Events.DESTROY, () => this.runCleanup());
3133
3135
  }
3134
3136
  /** 执行全部清理:解绑所有 bind 的订阅 + 清空场景调度器,并重置登记表 */
3135
3137
  runCleanup() {
@@ -3213,11 +3215,18 @@ var DEFAULTS = {
3213
3215
  fontSize: 32,
3214
3216
  fontFamily: "sans-serif"
3215
3217
  };
3216
- var Button = class extends Phaser4.GameObjects.Container {
3218
+ var Button = class extends Phaser2.GameObjects.Container {
3217
3219
  constructor(scene, config) {
3218
3220
  super(scene, config.x ?? 0, config.y ?? 0);
3219
3221
  __publicField(this, "bg");
3220
3222
  __publicField(this, "label");
3223
+ /**
3224
+ * 透明输入命中层(Image,使用默认 frame 命中区)。
3225
+ * 不直接在容器上 setInteractive(自定义 Rectangle) 的原因:真机/高分屏 Phaser FIT
3226
+ * 非整数缩放时,容器自定义矩形命中区会随离中心距离产生偏移,小按钮点不准;
3227
+ * Image 的 frame 命中区不受此影响。故输入统一走这一层。
3228
+ */
3229
+ __publicField(this, "hit");
3221
3230
  __publicField(this, "style");
3222
3231
  __publicField(this, "onClick");
3223
3232
  /** 是否禁用 */
@@ -3254,15 +3263,10 @@ var Button = class extends Phaser4.GameObjects.Container {
3254
3263
  this.label.setOrigin(0.5, 0.5);
3255
3264
  this.add(this.label);
3256
3265
  this.setSize(this.style.width, this.style.height);
3257
- this.setInteractive(
3258
- new Phaser4.Geom.Rectangle(
3259
- -this.style.width / 2,
3260
- -this.style.height / 2,
3261
- this.style.width,
3262
- this.style.height
3263
- ),
3264
- Phaser4.Geom.Rectangle.Contains
3265
- );
3266
+ this.hit = scene.make.image({ x: 0, y: 0, key: "__WHITE" }, false);
3267
+ this.hit.setDisplaySize(this.style.width, this.style.height).setAlpha(1e-3);
3268
+ this.hit.setInteractive({ useHandCursor: true });
3269
+ this.add(this.hit);
3266
3270
  this.bindEvents();
3267
3271
  scene.add.existing(this);
3268
3272
  }
@@ -3276,12 +3280,12 @@ var Button = class extends Phaser4.GameObjects.Container {
3276
3280
  this._disabled = disabled;
3277
3281
  this.setAlpha(disabled ? 0.5 : 1);
3278
3282
  if (disabled) {
3279
- this.disableInteractive();
3283
+ this.hit.disableInteractive();
3280
3284
  this.pressed = false;
3281
3285
  this.drawBackground(this.style.backgroundColor);
3282
3286
  this.setScale(1);
3283
3287
  } else {
3284
- this.setInteractive();
3288
+ this.hit.setInteractive({ useHandCursor: true });
3285
3289
  }
3286
3290
  return this;
3287
3291
  }
@@ -3295,25 +3299,25 @@ var Button = class extends Phaser4.GameObjects.Container {
3295
3299
  this.bg.fillStyle(color, 1);
3296
3300
  this.bg.fillRoundedRect(-width / 2, -height / 2, width, height, radius);
3297
3301
  }
3298
- /** 绑定指针交互:悬停/按下/抬起,实现按下态与点击触发 */
3302
+ /** 绑定指针交互:悬停/按下/抬起,实现按下态与点击触发(事件挂在命中层 hit 上) */
3299
3303
  bindEvents() {
3300
- this.on(Phaser4.Input.Events.POINTER_OVER, () => {
3304
+ this.hit.on(Phaser2.Input.Events.POINTER_OVER, () => {
3301
3305
  if (this._disabled) return;
3302
3306
  this.drawBackground(this.style.backgroundColorActive);
3303
3307
  });
3304
- this.on(Phaser4.Input.Events.POINTER_OUT, () => {
3308
+ this.hit.on(Phaser2.Input.Events.POINTER_OUT, () => {
3305
3309
  if (this._disabled) return;
3306
3310
  this.pressed = false;
3307
3311
  this.drawBackground(this.style.backgroundColor);
3308
3312
  this.setScale(1);
3309
3313
  });
3310
- this.on(Phaser4.Input.Events.POINTER_DOWN, () => {
3314
+ this.hit.on(Phaser2.Input.Events.POINTER_DOWN, () => {
3311
3315
  if (this._disabled) return;
3312
3316
  this.pressed = true;
3313
3317
  this.drawBackground(this.style.backgroundColorActive);
3314
3318
  this.setScale(0.96);
3315
3319
  });
3316
- this.on(Phaser4.Input.Events.POINTER_UP, () => {
3320
+ this.hit.on(Phaser2.Input.Events.POINTER_UP, () => {
3317
3321
  if (this._disabled) return;
3318
3322
  this.setScale(1);
3319
3323
  this.drawBackground(this.style.backgroundColor);
@@ -3324,6 +3328,81 @@ var Button = class extends Phaser4.GameObjects.Container {
3324
3328
  });
3325
3329
  }
3326
3330
  };
3331
+ var DEFAULT_SIZE = 88;
3332
+ var DEFAULT_BG = 2305092;
3333
+ var IconButton = class extends Phaser2.GameObjects.Container {
3334
+ constructor(scene, config) {
3335
+ super(scene, config.x ?? 0, config.y ?? 0);
3336
+ __publicField(this, "icon");
3337
+ /** 透明命中层(frame 命中区,真机/高分屏可靠) */
3338
+ __publicField(this, "hit");
3339
+ __publicField(this, "onClick");
3340
+ __publicField(this, "_disabled", false);
3341
+ __publicField(this, "pressed", false);
3342
+ const size = config.size ?? DEFAULT_SIZE;
3343
+ this.onClick = config.onClick;
3344
+ if (config.bgColor !== null) {
3345
+ const bg = scene.make.graphics({}, false);
3346
+ bg.fillStyle(config.bgColor ?? DEFAULT_BG, config.bgAlpha ?? 1);
3347
+ bg.fillCircle(0, 0, size / 2);
3348
+ this.add(bg);
3349
+ }
3350
+ this.icon = scene.make.image({ x: 0, y: 0, key: config.texture, frame: config.frame }, false);
3351
+ const target = size * (config.iconScale ?? 0.56);
3352
+ this.icon.setScale(target / Math.max(this.icon.width, this.icon.height || 1));
3353
+ if (config.flipX) this.icon.setFlipX(true);
3354
+ if (config.iconTint !== void 0) this.icon.setTint(config.iconTint);
3355
+ this.add(this.icon);
3356
+ this.hit = scene.make.image({ x: 0, y: 0, key: "__WHITE" }, false);
3357
+ this.hit.setDisplaySize(size, size).setAlpha(1e-3);
3358
+ this.hit.setInteractive({ useHandCursor: true });
3359
+ this.add(this.hit);
3360
+ this.setSize(size, size);
3361
+ this.bindEvents();
3362
+ scene.add.existing(this);
3363
+ }
3364
+ /** 更新图标纹理 */
3365
+ setIcon(texture, frame) {
3366
+ this.icon.setTexture(texture, frame);
3367
+ return this;
3368
+ }
3369
+ /** 设置禁用态 */
3370
+ setDisabled(disabled) {
3371
+ this._disabled = disabled;
3372
+ this.setAlpha(disabled ? 0.5 : 1);
3373
+ if (disabled) {
3374
+ this.hit.disableInteractive();
3375
+ this.pressed = false;
3376
+ this.setScale(1);
3377
+ } else {
3378
+ this.hit.setInteractive({ useHandCursor: true });
3379
+ }
3380
+ return this;
3381
+ }
3382
+ get disabled() {
3383
+ return this._disabled;
3384
+ }
3385
+ bindEvents() {
3386
+ this.hit.on(Phaser2.Input.Events.POINTER_DOWN, () => {
3387
+ if (this._disabled) return;
3388
+ this.pressed = true;
3389
+ this.setScale(0.92);
3390
+ });
3391
+ this.hit.on(Phaser2.Input.Events.POINTER_OUT, () => {
3392
+ if (this._disabled) return;
3393
+ this.pressed = false;
3394
+ this.setScale(1);
3395
+ });
3396
+ this.hit.on(Phaser2.Input.Events.POINTER_UP, () => {
3397
+ if (this._disabled) return;
3398
+ this.setScale(1);
3399
+ if (this.pressed) {
3400
+ this.pressed = false;
3401
+ this.onClick?.();
3402
+ }
3403
+ });
3404
+ }
3405
+ };
3327
3406
 
3328
3407
  // src/store/Store.ts
3329
3408
  function deepClone2(value) {
@@ -3812,4 +3891,4 @@ var MessageChannel = class {
3812
3891
  }
3813
3892
  };
3814
3893
 
3815
- export { App, BaseModule, BasePanel, BaseScene, Button, ConfigTable, ConfigTableManager, ErrorCode, FrameworkError, HttpClient, HttpError, I18n, JsonCodec, KcpClient, Layers, LogLevel, MessageChannel, MessageError, ModuleRegistry, NetManager, ObjectPool, PanelManager, PlatformContext, SaveManager, Scheduler, TypedEventBus, WebSocketClient, createStore, createTypedEventBus, err, isFrameworkError, normalizeLocale, ok, startGame };
3894
+ export { App, BaseModule, BasePanel, BaseScene, Button, ConfigTable, ConfigTableManager, ErrorCode, FrameworkError, HttpClient, HttpError, I18n, IconButton, JsonCodec, KcpClient, Layers, LogLevel, MessageChannel, MessageError, ModuleRegistry, NetManager, ObjectPool, PanelManager, PlatformContext, SaveManager, Scheduler, TypedEventBus, WebSocketClient, createStore, createTypedEventBus, err, isFrameworkError, normalizeLocale, ok, startGame };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maoyugames/phaser-framework",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "多平台 Phaser 游戏框架:业务/底层分离,HTTP/WebSocket/KCP,Web/TikTok/微信/Facebook/App 隔离打包",
5
5
  "type": "module",
6
6
  "license": "MIT",