Skip to content

Commit 15629b8

Browse files
committed
stage1, stage2, intro
1 parent 219f1e8 commit 15629b8

9 files changed

Lines changed: 247 additions & 273 deletions

File tree

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"@testing-library/react": "^13.4.0",
88
"@testing-library/user-event": "^13.5.0",
99
"cors": "^2.8.5",
10+
"espress": "^0.0.0",
1011
"express": "^4.21.1",
1112
"jsonwebtoken": "^9.0.2",
1213
"react": "^18.3.1",

public/images/stage1.png

2.27 MB
Loading

src/game/stage1.jsx

Lines changed: 146 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
import React, { useState, useEffect } from 'react';
22

33
const Stage1 = ({ onClose }) => {
4-
// State for answer and feedback
4+
// State for answer, feedback, hints, and drag
55
const [answer, setAnswer] = useState('');
66
const [feedback, setFeedback] = useState('');
7+
const [hint, setHint] = useState('');
8+
const [hint1Used, setHint1Used] = useState(false);
9+
const [hint2Used, setHint2Used] = useState(false);
10+
const [draggingCard, setDraggingCard] = useState(null);
11+
const [offset, setOffset] = useState({ x: 0, y: 0 });
712

813
// Card data for drag and drop
914
const cardData = [
10-
{ id: 1, text: 'ㅅ', width: 100, height: 100 },
11-
{ id: 2, text: 'ㅗ', width: 100, height: 100 },
12-
{ id: 3, text: 'ㄹ', width: 100, height: 100 },
13-
{ id: 4, text: 'ㅣ', width: 100, height: 100 },
14-
{ id: 5, text: 'ㄱ', width: 100, height: 100 },
15-
{ id: 6, text: 'ㅡ', width: 100, height: 100 },
16-
{ id: 7, text: 'ㄹ', width: 100, height: 100 },
17-
{ id: 8, text: 'ㅈ', width: 100, height: 100 },
18-
{ id: 9, text: 'ㅏ', width: 100, height: 100 },
15+
{ id: 1, text: 'ㅅ', width: 80, height: 80 },
16+
{ id: 2, text: 'ㅗ', width: 80, height: 80 },
17+
{ id: 3, text: 'ㄹ', width: 80, height: 80 },
18+
{ id: 4, text: 'ㅣ', width: 80, height: 80 },
19+
{ id: 5, text: 'ㄱ', width: 80, height: 80 },
20+
{ id: 6, text: 'ㅡ', width: 80, height: 80 },
21+
{ id: 7, text: 'ㄹ', width: 80, height: 80 },
22+
{ id: 8, text: 'ㅈ', width: 80, height: 80 },
23+
{ id: 9, text: 'ㅏ', width: 80, height: 80},
1924
];
2025

2126
// Initialize card positions randomly
@@ -27,82 +32,121 @@ const Stage1 = ({ onClose }) => {
2732
}))
2833
);
2934

30-
// Reset the cards' positions and answer
3135
useEffect(() => {
3236
handleReset();
3337
}, []);
3438

3539
const handleReset = () => {
3640
setCards(
37-
cards.map((card) => {
38-
const randomX = Math.random() * (800 - card.width);
39-
const randomY = Math.random() * (400 - card.height);
40-
return { ...card, left: randomX, top: randomY };
41-
})
41+
cardData.map((card) => ({
42+
...card,
43+
left: Math.random() * (800 - card.width),
44+
top: Math.random() * (400 - card.height),
45+
}))
4246
);
4347
setAnswer('');
4448
setFeedback('');
49+
setHint('');
50+
setHint1Used(false);
51+
setHint2Used(false);
4552
};
4653

47-
const handleDragStart = (e, cardId) => {
48-
e.dataTransfer.setData('text/plain', cardId);
49-
};
50-
51-
const handleDrop = (e) => {
52-
e.preventDefault();
53-
const cardId = e.dataTransfer.getData('text/plain');
54-
const cardIndex = cards.findIndex((card) => card.id === Number(cardId));
55-
const x = e.clientX - e.target.offsetLeft - cards[cardIndex].width / 2;
56-
const y = e.clientY - e.target.offsetTop - cards[cardIndex].height / 2;
57-
58-
setCards((prevCards) =>
59-
prevCards.map((card, index) =>
60-
index === cardIndex ? { ...card, left: x, top: y } : card
61-
)
62-
);
54+
const handleHint = (hintLevel) => {
55+
if (hintLevel === 1) {
56+
setHint('음성에 대응하는 문자를 가리키는 말');
57+
setHint1Used(true);
58+
} else if (hintLevel === 2 && hint1Used) {
59+
setHint('초성: ㅅㄹㄱㅈ');
60+
setHint2Used(true);
61+
} else if (hintLevel === 3 && hint2Used) {
62+
setHint('정답은 "소리글자"입니다.');
63+
}
6364
};
6465

65-
const handleDragOver = (e) => {
66-
e.preventDefault();
66+
const handleCloseHint = () => {
67+
setHint('');
6768
};
6869

6970
const handleSubmit = () => {
7071
const correctAnswer = '소리글자';
7172
if (answer === correctAnswer) {
7273
setFeedback('정답입니다! 🎉');
7374
setTimeout(() => {
74-
onClose(); // Navigate back to the dashboard
75+
onClose();
7576
}, 1000);
7677
} else {
7778
setFeedback('틀렸습니다. 다시 시도하세요.');
7879
}
7980
};
8081

82+
const handleMouseDown = (e, cardId) => {
83+
const card = cards.find((c) => c.id === cardId);
84+
if (card) {
85+
setDraggingCard(cardId);
86+
setOffset({ x: e.clientX - card.left, y: e.clientY - card.top });
87+
}
88+
};
89+
90+
const handleMouseMove = (e) => {
91+
if (draggingCard) {
92+
setCards((prevCards) =>
93+
prevCards.map((card) =>
94+
card.id === draggingCard
95+
? {
96+
...card,
97+
left: Math.min(
98+
Math.max(0, e.clientX - offset.x),
99+
1500 - card.width
100+
),
101+
top: Math.min(
102+
Math.max(0, e.clientY - offset.y),
103+
800 - card.height
104+
),
105+
}
106+
: card
107+
)
108+
);
109+
}
110+
};
111+
112+
const handleMouseUp = () => {
113+
setDraggingCard(null);
114+
};
115+
81116
return (
82-
<div className="stage-container" style={{ padding: '20px' }}>
117+
<div
118+
className="stage-container"
119+
style={{
120+
padding: '20px',
121+
userSelect: 'none',
122+
cursor: draggingCard ? 'grabbing' : 'default',
123+
}}
124+
onMouseMove={handleMouseMove}
125+
onMouseUp={handleMouseUp}
126+
>
83127
<header>
84128
<h1>훈민정음 맞추기 게임</h1>
85129
<p>전통 문화를 느끼며 단어를 맞춰보세요!</p>
86130
</header>
87131

88132
<div
89133
className="drop-area"
90-
onDrop={handleDrop}
91-
onDragOver={handleDragOver}
92134
style={{
93135
position: 'relative',
94-
width: '800px',
95-
height: '400px',
136+
width: '1200px',
137+
height: '450px',
96138
border: '2px dashed #000',
97139
margin: '20px auto',
140+
backgroundImage: 'url(/images/stage1.png)',
141+
backgroundSize: 'cover', // 이미지를 영역에 맞게 조정
142+
backgroundPosition: 'center', // 이미지를 중앙에 배치
98143
}}
99144
>
100145
{cards.map((card) => (
101146
<div
102147
key={card.id}
103148
className="card"
104-
draggable
105-
onDragStart={(e) => handleDragStart(e, card.id)}
149+
onMouseDown={(e) => handleMouseDown(e, card.id)}
106150
style={{
107151
position: 'absolute',
108152
left: `${card.left}px`,
@@ -112,8 +156,11 @@ const Stage1 = ({ onClose }) => {
112156
display: 'flex',
113157
justifyContent: 'center',
114158
alignItems: 'center',
115-
backgroundColor: '#bebebe',
116-
borderRadius: '5px',
159+
backgroundColor: '#ffe9b9c5',
160+
borderRadius: '20px',
161+
cursor: 'pointer',
162+
fontSize: '24px',
163+
fontWeight: 'bold',
117164
}}
118165
>
119166
{card.text}
@@ -143,8 +190,63 @@ const Stage1 = ({ onClose }) => {
143190
<button onClick={onClose} style={{ margin: '5px' }}>
144191
돌아가기
145192
</button>
193+
<button onClick={() => handleHint(1)}>힌트1</button>
194+
<button
195+
onClick={() => handleHint(2)}
196+
disabled={!hint1Used}
197+
style={{
198+
backgroundColor: hint1Used ? '#007bff' : '#ccc',
199+
cursor: hint1Used ? 'pointer' : 'not-allowed',
200+
}}
201+
>
202+
힌트2
203+
</button>
204+
<button
205+
onClick={() => handleHint(3)}
206+
disabled={!hint2Used}
207+
style={{
208+
backgroundColor: hint2Used ? '#28a745' : '#ccc',
209+
cursor: hint2Used ? 'pointer' : 'not-allowed',
210+
}}
211+
>
212+
정답
213+
</button>
146214
{feedback && <p>{feedback}</p>}
147215
</div>
216+
217+
{hint && (
218+
<div
219+
className="hint-modal"
220+
style={{
221+
position: 'fixed',
222+
top: '50%',
223+
left: '50%',
224+
transform: 'translate(-50%, -50%)',
225+
width: '400px',
226+
backgroundColor: 'white',
227+
padding: '20px',
228+
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.2)',
229+
borderRadius: '8px',
230+
zIndex: 1000,
231+
}}
232+
>
233+
<p>{hint}</p>
234+
<button
235+
onClick={handleCloseHint}
236+
style={{
237+
position: 'absolute',
238+
top: '10px',
239+
right: '10px',
240+
background: 'transparent',
241+
border: 'none',
242+
fontSize: '18px',
243+
cursor: 'pointer',
244+
}}
245+
>
246+
247+
</button>
248+
</div>
249+
)}
148250
</div>
149251
);
150252
};

0 commit comments

Comments
 (0)