基于PixiJS的Words of Wonder类单词游戏拖拽选字问题求助
修复PixiJS拖拽选中路径字母的问题
我正在用PixiJS开发一款类似Words of Wonder的单词游戏,当前代码只能检测拖拽的起始和结束动作,无法识别拖拽过程中经过的字母,求修复方案。
解决方案
要实现拖拽路径上的字母选中,核心是在拖拽过程中实时检测鼠标位置与字母的碰撞,具体实现如下:
- 全局监听拖拽移动事件:在PIXI舞台绑定
pointermove事件,实时追踪鼠标位置 - 维护拖拽状态:用变量标记是否处于拖拽中,避免非拖拽状态下的误触发
- 给字母元素添加标识:为每个字母文本添加
letter属性,方便快速获取字母内容 - 碰撞检测逻辑:在移动事件中遍历所有字母,判断鼠标是否进入字母的交互区域,未选中的字母加入选中数组
- 拖拽结束重置:清空选中状态,恢复所有字母的可交互性
修改后的完整代码
window.onload = () => { const container = document.getElementById('canvas-container'); const app = new PIXI.Application({ width: container.clientWidth * 2, height: container.clientHeight * 2, backgroundColor: 0x1099bb, }); container.appendChild(app.view); const background = PIXI.Sprite.from('https://cdn.glitch.global/b457296f-514e-47e5-82c6-28bdeeda16ca/background.png?v=1716472339788'); background.width = app.screen.width; background.height = app.screen.height; app.stage.addChild(background); const gridContent = [ [null, null, null, null], [null, '', null, ''], [null, null, null, ''], ]; const gridSize = app.screen.width / 5.3; const gridPadding = gridSize / 10; const letters = ['L', 'G', 'O', 'D']; const angleIncrement = (Math.PI * 2) / letters.length; const circleCenterX = app.screen.width / 2; const circleCenterY = app.screen.height - app.screen.width / 3 - 170; const circleRadius = app.screen.width / 3.3; const letterRadius = circleRadius / 4.2; const letterColors = ['#ff9933', '#ff9933', '#ff9933', '#ff9933']; const numRows = gridContent.length; const numCols = gridContent[0].length; const gridStartX = (app.screen.width - (numCols * (gridSize + gridPadding))) / 2; const gridGapY = 120; const gridStartY = gridGapY; // 维护拖拽状态和选中字母数组 let isDragging = false; let selectedLettersArray = []; // 全局拖拽移动事件,检测经过的字母 app.stage.on('pointermove', (event) => { if (!isDragging) return; const globalPos = event.data.global; // 获取所有带标识的字母文本元素 const letterTexts = app.stage.children.filter(child => child instanceof PIXI.Text && child.letter); letterTexts.forEach(text => { // 检测鼠标是否在字母的碰撞范围内(基于文本边界框) const bounds = text.getBounds(); if ( globalPos.x >= bounds.x && globalPos.x <= bounds.x + bounds.width && globalPos.y >= bounds.y && globalPos.y <= bounds.y + bounds.height && !text.isSelected ) { // 标记为已选中,加入数组 text.isSelected = true; selectedLettersArray.push(text.letter); console.log('选中字母:', text.letter, '当前选中序列:', selectedLettersArray); checkForValidWord(); } }); }); function checkForValidWord() { const selectedLetters = selectedLettersArray.join(''); console.log('检查有效单词:', selectedLetters); const foundAnswer = correctAnswers.find(answer => selectedLetters.endsWith(answer)); if (foundAnswer) { console.log('找到有效单词:', foundAnswer); fillGrid(foundAnswer, selectedLetters.length - foundAnswer.length); // 重置选中状态 resetSelectionState(); } } // 重置选中状态 function resetSelectionState() { selectedLettersArray = []; const letterTexts = app.stage.children.filter(child => child instanceof PIXI.Text && child.letter); letterTexts.forEach(text => { text.isSelected = false; text.interactive = true; }); isDragging = false; } function drawLetterWheel() { const shuffleButtonUrl = 'images/shuffle.png'; const shuffleButtonSize = 65; const circle = new PIXI.Graphics(); circle.beginFill(0xfff7e6, 0.7); circle.drawCircle(circleCenterX, circleCenterY, circleRadius); circle.endFill(); app.stage.addChild(circle); letters.forEach((letter, index) => { const angle = index * angleIncrement; const letterShiftFactor = 0.8; const x = circleCenterX + Math.cos(angle) * (circleRadius - letterRadius) * letterShiftFactor; const y = circleCenterY + Math.sin(angle) * (circleRadius - letterRadius) * letterShiftFactor; const letterText = new PIXI.Text(letter, { fontSize: letterRadius * 2, fill: letterColors[index], fontFamily: ['Poetsen One', 'Roboto', 'sans-serif'] }); letterText.anchor.set(0.5); letterText.position.set(x, y); // 给字母添加标识和选中状态 letterText.letter = letter; letterText.isSelected = false; app.stage.addChild(letterText); letterText.interactive = true; letterText.buttonMode = true; letterText .on('pointerdown', onDragStart) .on('pointerup', onDragEnd) .on('pointerupoutside', onDragEnd); function onDragStart() { console.log('拖拽开始:', letter); isDragging = true; // 标记当前字母为已选中,加入数组 letterText.isSelected = true; selectedLettersArray.push(letter); console.log('当前选中序列:', selectedLettersArray); checkForValidWord(); letterText.interactive = false; // 选中后暂时禁用交互 } function onDragEnd() { console.log('拖拽结束'); resetSelectionState(); } }); const shuffleButton = PIXI.Sprite.from(shuffleButtonUrl); shuffleButton.anchor.set(0.5); shuffleButton.width = shuffleButton.height = shuffleButtonSize; shuffleButton.position.set(circleCenterX, circleCenterY); shuffleButton.interactive = true; shuffleButton.buttonMode = true; shuffleButton.on('pointerdown', () => { shuffleLetters(); }); app.stage.addChild(shuffleButton); } const correctAnswers = ['GOLD', 'DOG', 'GOD', 'LOG']; function drawGrid() { for (let row = 0; row < numRows; row++) { for (let col = 0; col < numCols; col++) { if (gridContent[row][col] === null) { const cell = new PIXI.Graphics(); cell.beginFill(0xfff7e6); cell.drawRoundedRect(0, 0, gridSize, gridSize, 20); cell.endFill(); cell.position.set(gridStartX + col * (gridSize + gridPadding), gridStartY + row * (gridSize + gridPadding)); app.stage.addChild(cell); } } } } function fillGrid(answer, startIndex) { for (let i = 0; i < answer.length; i++) { const row = Math.floor(startIndex / 4); const col = startIndex % 4; const cell = app.stage.children.find(child => child instanceof PIXI.Graphics && child.position.x === col * (gridSize + gridPadding) + gridStartX && child.position.y === row * (gridSize + gridPadding) + gridStartY); if (cell) { const text = new PIXI.Text(answer[i], { fill: 'black' }); text.anchor.set(0.5); text.position.set(cell.x + gridSize / 2, cell.y + gridSize / 2); app.stage.addChild(text); } startIndex++; } } function shuffleLetters() { const shuffleLetters = ['L', 'G', 'O', 'D']; for (let i = shuffleLetters.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffleLetters[i], shuffleLetters[j]] = [shuffleLetters[j], shuffleLetters[i]]; } const letterTexts = app.stage.children.filter(child => child instanceof PIXI.Text && child.letter); shuffleLetters.forEach((letter, index) => { letterTexts[index].text = letter; letterTexts[index].letter = letter; // 更新字母标识 letterTexts[index].isSelected = false; letterTexts[index].interactive = true; }); } drawGrid(); drawLetterWheel(); };
内容的提问来源于stack exchange,提问作者beilavi
相关产品推荐
相关产品推荐

