| | |
| | | import { DEVICE_TYPE, MAP_MIRROR, MAP_TONES } from './constants'; |
| | | |
| | | const getMapTone = (mode) => mode === 'dark' ? MAP_TONES.dark : MAP_TONES.light; |
| | | const normalizeDegrees = (degrees) => ((degrees % 360) + 360) % 360; |
| | | |
| | | export default class Player { |
| | | |
| | |
| | | this.mapContainer.pivot.set(centerX, centerY); |
| | | this.mapContainer.position.set(this.app.renderer.width / 2, this.app.renderer.height / 2); |
| | | |
| | | const newRotation = this.mapContainer.rotation + value; |
| | | |
| | | const rotationDegrees = (newRotation * 180 / Math.PI) % 360; |
| | | this.rotationText.text = `{ ROTATION: ${rotationDegrees.toFixed(1)}° }`; |
| | | const rotationOffset = Number(value) || 0; |
| | | const newRotation = this.mapContainer.rotation + rotationOffset; |
| | | |
| | | new TWEEDLE.Tween(this.mapContainer) |
| | | .to({ rotation: newRotation }, 200) |
| | | .easing(TWEEDLE.Easing.Quadratic.Out) |
| | | .onUpdate(() => { |
| | | this.updateRotationHud(this.mapContainer.rotation); |
| | | }) |
| | | .onComplete(() => { |
| | | this.updateRotationHud(this.mapContainer.rotation); |
| | | localStorage.setItem('mapRotation', newRotation % (Math.PI * 2)); |
| | | }) |
| | | .start(); |
| | |
| | | this.rotationText.position.set(10, 35); |
| | | this.app.stage.addChild(this.rotationText); |
| | | |
| | | this.createDirectionHud(tone); |
| | | |
| | | this.app.stage.on('pointermove', (event) => { |
| | | const worldPos = event.data.getLocalPosition(this.mapContainer); |
| | | this.coordinatesText.text = `{ X: ${worldPos.x.toFixed(2)}, Y: ${worldPos.y.toFixed(2)} }`; |
| | | }); |
| | | } |
| | | |
| | | getDirectionHudTone = () => { |
| | | if (this.themeMode === 'dark') { |
| | | return { |
| | | ring: 0x7f8ea3, |
| | | axis: 0x5d6980, |
| | | accent: 0x49a6ff, |
| | | textStroke: 0x1c222b, |
| | | }; |
| | | } |
| | | return { |
| | | ring: 0xaab4c2, |
| | | axis: 0xd6dde7, |
| | | accent: 0x2f68ac, |
| | | textStroke: 0xf1f2f6, |
| | | }; |
| | | } |
| | | |
| | | createDirectionHud = (tone) => { |
| | | const hudTone = this.getDirectionHudTone(); |
| | | this.directionHud = new PIXI.Container(); |
| | | this.directionHud.name = 'directionHud'; |
| | | this.directionHud.position.set(16, 68); |
| | | this.app.stage.addChild(this.directionHud); |
| | | |
| | | this.directionHudDial = new PIXI.Container(); |
| | | this.directionHudDial.position.set(52, 42); |
| | | this.directionHud.addChild(this.directionHudDial); |
| | | |
| | | this.directionHudDialGraphics = new PIXI.Graphics(); |
| | | this.directionHudDial.addChild(this.directionHudDialGraphics); |
| | | |
| | | this.directionHudLabels = {}; |
| | | [0, 90, 180, 270].forEach((degree) => { |
| | | const label = new PIXI.Text(`${degree}°`, { |
| | | fill: tone.textPrimary, |
| | | fontSize: 13, |
| | | fontFamily: 'Microsoft YaHei', |
| | | fontWeight: 'bold', |
| | | stroke: hudTone.textStroke, |
| | | strokeThickness: 3, |
| | | }); |
| | | label.anchor.set(0.5); |
| | | this.directionHudDial.addChild(label); |
| | | this.directionHudLabels[degree] = label; |
| | | }); |
| | | |
| | | this.updateDirectionHudTheme(); |
| | | this.updateRotationHud(0); |
| | | } |
| | | |
| | | updateRotationHud = (rotation = 0) => { |
| | | const rotationDegrees = normalizeDegrees(rotation * 180 / Math.PI); |
| | | if (this.rotationText) { |
| | | this.rotationText.text = `{ ROTATION: ${rotationDegrees.toFixed(1)}° }`; |
| | | } |
| | | this.updateDirectionHud(rotationDegrees); |
| | | } |
| | | |
| | | updateDirectionHud = (rotationDegrees = 0) => { |
| | | if (!this.directionHudDial || !this.directionHudLabels) { |
| | | return; |
| | | } |
| | | |
| | | const radius = 28; |
| | | const centerLabelRadius = 48; |
| | | |
| | | [0, 90, 180, 270].forEach((degree) => { |
| | | const screenAngle = normalizeDegrees(degree + rotationDegrees); |
| | | const angle = screenAngle * Math.PI / 180; |
| | | const x = Math.cos(angle) * centerLabelRadius; |
| | | const y = Math.sin(angle) * centerLabelRadius; |
| | | const label = this.directionHudLabels[degree]; |
| | | if (label) { |
| | | label.position.set(x, y); |
| | | } |
| | | }); |
| | | |
| | | if (this.directionHudDialGraphics) { |
| | | const hudTone = this.getDirectionHudTone(); |
| | | this.directionHudDialGraphics.clear(); |
| | | this.directionHudDialGraphics.lineStyle(1.5, hudTone.axis, 0.85); |
| | | this.directionHudDialGraphics.moveTo(-radius, 0); |
| | | this.directionHudDialGraphics.lineTo(radius, 0); |
| | | this.directionHudDialGraphics.moveTo(0, -radius); |
| | | this.directionHudDialGraphics.lineTo(0, radius); |
| | | this.directionHudDialGraphics.lineStyle(2, hudTone.ring, 0.95); |
| | | this.directionHudDialGraphics.drawCircle(0, 0, radius); |
| | | this.directionHudDialGraphics.beginFill(hudTone.accent, 1); |
| | | this.directionHudDialGraphics.moveTo(0, -radius - 10); |
| | | this.directionHudDialGraphics.lineTo(-6, -radius + 2); |
| | | this.directionHudDialGraphics.lineTo(6, -radius + 2); |
| | | this.directionHudDialGraphics.lineTo(0, -radius - 10); |
| | | this.directionHudDialGraphics.endFill(); |
| | | this.directionHudDialGraphics.beginFill(hudTone.accent, 0.14); |
| | | this.directionHudDialGraphics.drawCircle(0, 0, 7); |
| | | this.directionHudDialGraphics.endFill(); |
| | | } |
| | | } |
| | | |
| | | updateDirectionHudTheme = () => { |
| | | const tone = getMapTone(this.themeMode); |
| | | const hudTone = this.getDirectionHudTone(); |
| | | |
| | | if (this.directionHudLabels) { |
| | | Object.values(this.directionHudLabels).forEach((label) => { |
| | | label.style.fill = tone.textPrimary; |
| | | label.style.stroke = hudTone.textStroke; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | if (this.rotationText) { |
| | | this.rotationText.style.fill = tone.textPrimary; |
| | | } |
| | | if (this.directionHud) { |
| | | this.updateDirectionHudTheme(); |
| | | this.updateRotationHud(this.mapContainer.rotation); |
| | | } |
| | | if (this.gridLineContainer) { |
| | | this.showGridLines(); |
| | | } |