赞
踩
小时候经常玩连连看小游戏。在游戏中,当找到2个相同的元素就可以消除元素。
本文会借助react实现连连看小游戏。
1. 每一个图片都是一个item,items数组的大小为size*size。
item对象包括grid布局的位置,key。
key是标识符,可以标识图片, 相等判断等。
2. items 可以先顺序生成,最后再调用shuffle算法随机排序。
- const size = 8; // 大小为 8 * 8
-
- const itemImgSize = 20; // 圖片素材大小
- const [items, setItems] = useState([]);
- useEffect(() => { // 初始化元素
- const initItems = [];
- let idx = 0;
- while (initItems.length < size * size) {
- // 一次插入2個
- initItems.push({
- key: (idx % itemImgSize) + 1,
- x: parseInt(idx / size),
- y: parseInt(idx % size)
- });
-
- initItems.push({
- key: (( idx)% itemImgSize) + 1,
- x: parseInt(( idx + 1) / size),
- y: parseInt((idx + 1)% size)
- });
- idx = idx + 1;
- }
- const nArr = [...shuffleArray(initItems)];
- setItems(nArr);
-
- }, [])
-
-
- function shuffleArray(arr) {
-
- for (let i = arr.length - 1; i >= arr.length / 2; i--) {
- const j = Math.floor(Math.random() * (size - 1));
- [arr[i], arr[j]] = [arr[j], arr[i]];
- if (arr[i] instanceof Array) {
- shuffleArray(arr[i]);
- shuffleArray(arr[j])
- } else {
- // 交换key
- let key = arr[i].key;
- arr[i].key = arr[j].key;
- arr[j].key = key;
- }
- }
- return arr;
- }
基于dfs算法实现,以其中一方为原点,另一方为终点。找到一条成功的路径。
tips: dfs 可以改成bfs, dfs 当item 消除大半后,会变慢。
- /**
- * 判断 (i,j) 与(x,y)是否可达
- */
- function dfs(i, j, visited, x, y) {
- if (res.current === true) {
- return;
- }
- if (i < 0 || i >= size + 2 || j < 0 || j >= size + 2) { // 边界
- return;
- }
-
- if (i === x && j === y) {
- res.current = true;
- return;
- }
-
- if (visited[i][j] === 1) {
- return;
- }
- if (board.current[i][j] === 1) { // 只能走空白
- return;
- }
- visited[i][j] = 1;
- dfs(i - 1, j, visited, x, y);
- dfs(i + 1, j, visited, x, y);
- dfs(i, j + 1, visited, x, y);
- dfs(i, j - 1, visited, x, y);
- visited[i][j] = 0;
- }
boards标记数组是根据items数组生成,若item存在,则boards对应标记为1,反之为null。
item 对应位置为(item.x+1,item.y+1)
boards 的大小为(size + 2) * (size + 2) , + 2是为了解决边界上的2点相连处理。
- // 二维int数组,标记是否存在元素 (size + 2) * (size + 2), +1是为了边界可以连接
- const board = useRef([]);
- useEffect(() => {
- const nBoard = new Array(size + 2);
- // init
- for (let i = 0; i < size + 2; i++) {
- nBoard[i] = new Array(size + 2);
- for (let j = 0; j< size + 2;j++){
- nBoard[i][j] = 0;
- }
- }
-
- //根据items设置boards
- items.map((item) => {
- nBoard[item.x + 1][item.y + 1] = 1;
- })
-
- board.current = (nBoard)
- }, [items]);
- import bgImg from './imgs/bg.png'
- import {useEffect, useRef, useState} from "react";
-
- export const LinkGame = () => {
-
- const size = 8; // 大小为 8 * 8
-
- const itemImgSize = 20; // 圖片素材大小
- const [items, setItems] = useState([]);
- useEffect(() => { // 初始化元素
- const initItems = [];
- let idx = 0;
- while (initItems.length < size * size) {
- // 一次插入2個
- initItems.push({
- key: (idx % itemImgSize) + 1,
- x: parseInt(idx / size),
- y: parseInt(idx % size)
- });
-
- initItems.push({
- key: (idx % itemImgSize) + 1,
- x: parseInt((idx + 1)/ size),
- y: parseInt((idx + 1) % size)
- });
- idx = idx + 2;
- }
- const nArr = [...shuffleArray(initItems)];
- setItems(nArr);
-
- }, [])
-
-
- function shuffleArray(arr) {
-
- for (let i = arr.length - 1; i >= arr.length / 2; i--) {
- const j = Math.floor(Math.random() * (size - 1));
- [arr[i], arr[j]] = [arr[j], arr[i]];
- if (arr[i] instanceof Array) {
- shuffleArray(arr[i]);
- shuffleArray(arr[j])
- } else {
- // 交换key
- let key = arr[i].key;
- arr[i].key = arr[j].key;
- arr[j].key = key;
- }
- }
- return arr;
- }
-
- // 二维int数组,标记是否存在元素 (size + 2) * (size + 2), +1是为了边界可以连接
- const board = useRef([]);
- useEffect(() => {
- const nBoard = new Array(size + 2);
- // init
- for (let i = 0; i < size + 2; i++) {
- nBoard[i] = new Array(size + 2);
- for (let j = 0; j< size + 2;j++){
- nBoard[i][j] = 0;
- }
- }
-
- //根据items设置boards
- items.map((item) => {
- nBoard[item.x + 1][item.y + 1] = 1;
- })
-
- board.current = (nBoard)
- }, [items]);
-
-
- // 当选择2个时候,判断是否能消除,如果能消除,则消除,不能则复原。
- const res = useRef(false);
- useEffect(() => {
- const checkedList = [];
- items.map(item => {
- if (item.checked) {
- checkedList.push(item);
- }
- if (checkedList.length === 2) {
- const a = checkedList[0];
- const b = checkedList[1];
-
- if (a.key !== b.key) {
- a.checked = false;
- b.checked = false;
- setItems([...items])
- } else {
- // 判断 a 和 b 直接是否能连接
- const visited = new Array(size + 2);
- for (let i = 0; i < size + 2; i++) {
- visited[i] = new Array(size + 2);
- }
- const i = a.x + 1;
- const j = a.y + 1;
- const x = b.x + 1;
- const y = b.y + 1;
- dfs(i + 1, j, visited, x, y)
- dfs(i - 1, j, visited, x, y)
- dfs(i, j + 1, visited, x, y)
- dfs(i, j - 1, visited, x, y)
- if (res.current === true) { // 存在线路相连
- // 移除 a 和 b
- const nItems = [];
- items.map((item) => {
- if (item !== a && item !== b) {
- nItems.push(item);
- }
- })
- setItems(nItems)
- res.current = false; //init
- } else {
- a.checked = false;
- b.checked = false;
- setItems([...items])
- }
- }
- }
- })
- }, [items]);
-
- /**
- * 判断 (i,j) 与(x,y)是否可达
- */
- function dfs(i, j, visited, x, y) {
- if (res.current === true) {
- return;
- }
- if (i < 0 || i >= size + 2 || j < 0 || j >= size + 2) { // 边界
- return;
- }
-
- if (i === x && j === y) {
- res.current = true;
- return;
- }
-
- if (visited[i][j] === 1) {
- return;
- }
- if (board.current[i][j] === 1) { // 只能走空白
- return;
- }
- visited[i][j] = 1;
- dfs(i - 1, j, visited, x, y);
- dfs(i + 1, j, visited, x, y);
- dfs(i, j + 1, visited, x, y);
- dfs(i, j - 1, visited, x, y);
- visited[i][j] = 0;
- }
-
- function onItemClick(item) {
- item.checked = !item.checked;
- setItems([...items]);
- }
-
- const gameBoardStyle = { // 游戏区域样式
- display: 'grid',
- gridTemplateColumns: `repeat(${size}, 1fr)`,
- gridTemplateRows: `repeat(${size}, 1fr)`,
- width: '60vw',
- height: '80vh',
- backgroundImage: 'url(' + bgImg + ')',
- backgroundSize: 'cover'
- };
-
- const gameBoardItemStyle = (item) => {
- if (item.checked) {
- return ({
- gridRowStart: item.x + 1,
- gridColumnStart: item.y + 1,
- opacity: 0.4
- })
- }
- return ({
- gridRowStart: item.x + 1,
- gridColumnStart: item.y + 1,
-
- });
- }
-
- return <>
- <div id={'link-game'}>
- <div style={gameBoardStyle}>
- {
- items.map((item, idx) => (
- <div style={gameBoardItemStyle(item)}
- onClick={() => onItemClick(item)} key={'item-' + idx}>
- <img src={require(`./imgs/${item.key}.png`)}/>
- </div>
- ))
- }
- </div>
- </div>
- </>
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。