21 KiB
יצירת משחק באמצעות אירועים
חידון לפני ההרצאה
תכנות מונחה אירועים
כאשר אנו יוצרים אפליקציה מבוססת דפדפן, אנו מספקים ממשק משתמש גרפי (GUI) למשתמש כדי שיוכל לתקשר עם מה שבנינו. הדרך הנפוצה ביותר לתקשר עם הדפדפן היא באמצעות לחיצה והקלדה על אלמנטים שונים. האתגר שלנו כמפתחים הוא שאיננו יודעים מתי המשתמש יבצע את הפעולות הללו!
תכנות מונחה אירועים הוא השם לסוג התכנות שאנו צריכים לבצע כדי ליצור את ה-GUI שלנו. אם נפרק את הביטוי הזה, נראה שהמילה המרכזית כאן היא אירוע. אירוע, לפי Merriam-Webster, מוגדר כ"משהו שקורה". זה מתאר את המצב שלנו בצורה מושלמת. אנו יודעים שמשהו הולך לקרות, עבורו נרצה להפעיל קוד בתגובה, אך איננו יודעים מתי זה יקרה.
הדרך שבה אנו מסמנים קטע קוד שאנו רוצים להפעיל היא על ידי יצירת פונקציה. כשאנו חושבים על תכנות פרוצדורלי, פונקציות נקראות בסדר מסוים. אותו דבר נכון גם בתכנות מונחה אירועים. ההבדל הוא איך הפונקציות ייקראו.
כדי לטפל באירועים (לחיצה על כפתור, הקלדה וכו'), אנו רושמים מאזיני אירועים. מאזין אירועים הוא פונקציה שמאזינה לאירוע שמתרחש ומופעלת בתגובה. מאזיני אירועים יכולים לעדכן את ממשק המשתמש, לבצע קריאות לשרת, או כל דבר אחר שצריך לעשות בתגובה לפעולת המשתמש. אנו מוסיפים מאזין אירועים באמצעות addEventListener ומספקים פונקציה לביצוע.
NOTE: כדאי לציין שיש דרכים רבות ליצור מאזיני אירועים. ניתן להשתמש בפונקציות אנונימיות או ליצור פונקציות עם שם. ניתן להשתמש בקיצורי דרך שונים, כמו הגדרת המאפיין
click
או שימוש ב-addEventListener
. בתרגיל שלנו נתמקד ב-addEventListener
ובפונקציות אנונימיות, מכיוון שזה כנראה הטכניקה הנפוצה ביותר שמפתחים משתמשים בה. זה גם הכי גמיש, מכיוון ש-addEventListener
עובד עבור כל האירועים, ושם האירוע יכול להינתן כפרמטר.
אירועים נפוצים
ישנם עשרות אירועים שניתן להאזין להם בעת יצירת אפליקציה. למעשה, כל דבר שמשתמש עושה בדף מעלה אירוע, מה שנותן לך הרבה כוח לוודא שהוא מקבל את החוויה הרצויה. למרבה המזל, בדרך כלל תצטרך רק קומץ קטן של אירועים. הנה כמה נפוצים (כולל שניים שנשתמש בהם ביצירת המשחק שלנו):
- click: המשתמש לחץ על משהו, בדרך כלל כפתור או קישור
- contextmenu: המשתמש לחץ על כפתור העכבר הימני
- select: המשתמש סימן טקסט
- input: המשתמש הזין טקסט
יצירת המשחק
אנו הולכים ליצור משחק כדי לחקור כיצד אירועים עובדים ב-JavaScript. המשחק שלנו יבדוק את מיומנות ההקלדה של השחקן, שהיא אחת המיומנויות הכי פחות מוערכות שכל מפתח צריך. כולנו צריכים לתרגל את ההקלדה שלנו! הזרימה הכללית של המשחק תיראה כך:
- השחקן לוחץ על כפתור ההתחלה ומוצגת לו ציטוט להקלדה
- השחקן מקליד את הציטוט במהירות האפשרית בתיבת טקסט
- בכל פעם שמילה מושלמת, המילה הבאה מודגשת
- אם לשחקן יש שגיאת הקלדה, תיבת הטקסט מתעדכנת לאדום
- כאשר השחקן משלים את הציטוט, מוצגת הודעת הצלחה עם הזמן שחלף
בואו נבנה את המשחק שלנו ונלמד על אירועים!
מבנה הקבצים
נזדקק לשלושה קבצים בסך הכול: index.html, script.js ו-style.css. בואו נתחיל בהגדרתם כדי להקל עלינו.
- צרו תיקייה חדשה לעבודה שלכם על ידי פתיחת חלון קונסול או טרמינל והקלדת הפקודה הבאה:
# Linux or macOS
mkdir typing-game && cd typing-game
# Windows
md typing-game && cd typing-game
- פתחו את Visual Studio Code
code .
- הוסיפו שלושה קבצים לתיקייה ב-Visual Studio Code עם השמות הבאים:
- index.html
- script.js
- style.css
יצירת ממשק המשתמש
אם נבחן את הדרישות, אנו יודעים שנצטרך כמה אלמנטים בדף ה-HTML שלנו. זה קצת כמו מתכון, שבו אנו צריכים כמה מרכיבים:
- מקום להציג את הציטוט שהמשתמש צריך להקליד
- מקום להציג הודעות, כמו הודעת הצלחה
- תיבת טקסט להקלדה
- כפתור התחלה
לכל אחד מהם נצטרך להוסיף מזהים (IDs) כדי שנוכל לעבוד איתם ב-JavaScript שלנו. נוסיף גם הפניות לקבצי ה-CSS וה-JavaScript שניצור.
צרו קובץ חדש בשם index.html. הוסיפו את ה-HTML הבא:
<!-- inside index.html -->
<html>
<head>
<title>Typing game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Typing game!</h1>
<p>Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!</p>
<p id="quote"></p> <!-- This will display our quote -->
<p id="message"></p> <!-- This will display any status messages -->
<div>
<input type="text" aria-label="current word" id="typed-value" /> <!-- The textbox for typing -->
<button type="button" id="start">Start</button> <!-- To start the game -->
</div>
<script src="script.js"></script>
</body>
</html>
הפעלת האפליקציה
תמיד עדיף לפתח באופן איטרטיבי כדי לראות איך הדברים נראים. בואו נפעיל את האפליקציה שלנו. יש תוסף נהדר ל-Visual Studio Code בשם Live Server שיארח את האפליקציה שלכם מקומית וירענן את הדפדפן בכל פעם שתשמרו.
- התקינו את Live Server על ידי לחיצה על הקישור ולחיצה על Install
- הדפדפן יבקש מכם לפתוח את Visual Studio Code, ואז Visual Studio Code יבקש מכם לבצע את ההתקנה
- אתחלו את Visual Studio Code אם תתבקשו
- לאחר ההתקנה, ב-Visual Studio Code, לחצו על Ctrl-Shift-P (או Cmd-Shift-P) כדי לפתוח את לוח הפקודות
- הקלידו Live Server: Open with Live Server
- Live Server יתחיל לארח את האפליקציה שלכם
- פתחו דפדפן ונווטו לכתובת https://localhost:5500
- כעת אמור להופיע הדף שיצרתם!
בואו נוסיף קצת פונקציונליות.
הוספת ה-CSS
עם יצירת ה-HTML שלנו, נוסיף את ה-CSS לעיצוב הבסיסי. נצטרך להדגיש את המילה שהשחקן צריך להקליד ולצבוע את תיבת הטקסט אם מה שהוקלד שגוי. נעשה זאת באמצעות שתי מחלקות.
צרו קובץ חדש בשם style.css והוסיפו את התחביר הבא.
/* inside style.css */
.highlight {
background-color: yellow;
}
.error {
background-color: lightcoral;
border: red;
}
✅ כשמדובר ב-CSS, אתם יכולים לעצב את הדף שלכם איך שתרצו. קחו קצת זמן ותעשו את הדף יותר מושך:
- בחרו גופן שונה
- צבעו את הכותרות
- שנו את גודל האלמנטים
JavaScript
עם יצירת ממשק המשתמש שלנו, הגיע הזמן להתמקד ב-JavaScript שיספק את הלוגיקה. נחלק את זה לכמה שלבים:
אבל קודם, צרו קובץ חדש בשם script.js.
יצירת הקבועים
נזדקק לכמה פריטים כדי להקל עלינו בתכנות. שוב, בדומה למתכון, הנה מה שנצטרך:
- מערך עם רשימת כל הציטוטים
- מערך ריק לאחסון כל המילים של הציטוט הנוכחי
- מקום לאחסון האינדקס של המילה שהשחקן מקליד כרגע
- הזמן שבו השחקן לחץ על התחלה
נרצה גם הפניות לאלמנטים בממשק המשתמש:
- תיבת הטקסט (typed-value)
- תצוגת הציטוט (quote)
- ההודעה (message)
// inside script.js
// all of our quotes
const quotes = [
'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.',
'There is nothing more deceptive than an obvious fact.',
'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.',
'I never make exceptions. An exception disproves the rule.',
'What one man can invent another can discover.',
'Nothing clears up a case so much as stating it to another person.',
'Education never ends, Watson. It is a series of lessons, with the greatest for the last.',
];
// store the list of words and the index of the word the player is currently typing
let words = [];
let wordIndex = 0;
// the starting time
let startTime = Date.now();
// page elements
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message');
const typedValueElement = document.getElementById('typed-value');
✅ הוסיפו עוד ציטוטים למשחק שלכם
NOTE: אנו יכולים לאחזר את האלמנטים בכל פעם שנרצה בקוד באמצעות
document.getElementById
. מכיוון שנפנה לאלמנטים הללו באופן קבוע, נשתמש בקבועים כדי להימנע משגיאות הקלדה במחרוזות. מסגרות כמו Vue.js או React יכולות לעזור לכם לנהל טוב יותר את ריכוז הקוד שלכם.
קחו רגע לצפות בסרטון על שימוש ב-const
, let
ו-var
🎥 לחצו על התמונה למעלה לצפייה בסרטון על משתנים.
הוספת לוגיקת התחלה
כדי להתחיל את המשחק, השחקן ילחץ על התחלה. כמובן, איננו יודעים מתי הוא ילחץ על התחלה. כאן נכנס לתמונה מאזין אירועים. מאזין אירועים יאפשר לנו להאזין למשהו שמתרחש (אירוע) ולהפעיל קוד בתגובה. במקרה שלנו, נרצה להפעיל קוד כאשר המשתמש לוחץ על התחלה.
כאשר המשתמש לוחץ על start, נצטרך לבחור ציטוט, להגדיר את ממשק המשתמש ולהגדיר מעקב אחר המילה הנוכחית והזמן. להלן ה-JavaScript שתצטרכו להוסיף; נדון בו מיד לאחר קטע הקוד.
// at the end of script.js
document.getElementById('start').addEventListener('click', () => {
// get a quote
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// Put the quote into an array of words
words = quote.split(' ');
// reset the word index for tracking
wordIndex = 0;
// UI updates
// Create an array of span elements so we can set a class
const spanWords = words.map(function(word) { return `<span>${word} </span>`});
// Convert into string and set as innerHTML on quote display
quoteElement.innerHTML = spanWords.join('');
// Highlight the first word
quoteElement.childNodes[0].className = 'highlight';
// Clear any prior messages
messageElement.innerText = '';
// Setup the textbox
// Clear the textbox
typedValueElement.value = '';
// set focus
typedValueElement.focus();
// set the event handler
// Start the timer
startTime = new Date().getTime();
});
בואו נפרק את הקוד!
- הגדרת מעקב המילים
- שימוש ב-Math.floor וב-Math.random מאפשר לנו לבחור באופן אקראי ציטוט מתוך המערך
quotes
- אנו ממירים את ה-
quote
למערך שלwords
כדי שנוכל לעקוב אחר המילה שהשחקן מקליד כרגע wordIndex
מוגדר ל-0, מכיוון שהשחקן יתחיל במילה הראשונה
- שימוש ב-Math.floor וב-Math.random מאפשר לנו לבחור באופן אקראי ציטוט מתוך המערך
- הגדרת ממשק המשתמש
- יצירת מערך של
spanWords
, שמכיל כל מילה בתוך אלמנטspan
- זה יאפשר לנו להדגיש את המילה בתצוגה
- שימוש ב-
join
ליצירת מחרוזת שנוכל להשתמש בה כדי לעדכן את ה-innerHTML
שלquoteElement
- זה יציג את הציטוט לשחקן
- הגדרת
className
של אלמנט ה-span
הראשון ל-highlight
כדי להדגיש אותו בצהוב - ניקוי
messageElement
על ידי הגדרתinnerText
ל-''
- יצירת מערך של
- הגדרת תיבת הטקסט
- ניקוי ה-
value
הנוכחי שלtypedValueElement
- הגדרת ה-
focus
ל-typedValueElement
- ניקוי ה-
- התחלת הטיימר על ידי קריאה ל-
getTime
הוספת לוגיקת הקלדה
כאשר השחקן מקליד, יופעל אירוע input
. מאזין האירועים הזה יבדוק שהשחקן מקליד את המילה בצורה נכונה ויטפל במצב הנוכחי של המשחק. חזרו ל-script.js והוסיפו את הקוד הבא בסוף. נפרק אותו לאחר מכן.
// at the end of script.js
typedValueElement.addEventListener('input', () => {
// Get the current word
const currentWord = words[wordIndex];
// get the current value
const typedValue = typedValueElement.value;
if (typedValue === currentWord && wordIndex === words.length - 1) {
// end of sentence
// Display success
const elapsedTime = new Date().getTime() - startTime;
const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`;
messageElement.innerText = message;
} else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) {
// end of word
// clear the typedValueElement for the new word
typedValueElement.value = '';
// move to the next word
wordIndex++;
// reset the class name for all elements in quote
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// highlight the new word
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// currently correct
// highlight the next word
typedValueElement.className = '';
} else {
// error state
typedValueElement.className = 'error';
}
});
בואו נפרק את הקוד! אנו מתחילים באחזור המילה הנוכחית והערך שהשחקן הקליד עד כה. לאחר מכן יש לנו לוגיקה מדורגת, שבה אנו בודקים אם הציטוט הושלם, המילה הושלמה, המילה נכונה, או (לבסוף), אם יש שגיאה.
- הציטוט הושלם, מצוין על ידי כך ש-
typedValue
שווה ל-currentWord
, ו-wordIndex
שווה לאחד פחות מ-length
שלwords
- חישוב
elapsedTime
על ידי חיסורstartTime
מהזמן הנוכחי - חלוקת
elapsedTime
ב-1,000 כדי להמיר ממילישניות לשניות - הצגת הודעת הצלחה
- חישוב
- המילה הושלמה, מצוין על ידי כך ש-
typedValue
מסתיים ברווח (סוף מילה) ו-typedValue
שווה ל-currentWord
- הגדרת
value
שלtypedElement
ל-''
כדי לאפשר הקלדת המילה הבאה - הגדלת
wordIndex
כדי לעבור למילה הבאה - מעבר על כל ה-
childNodes
שלquoteElement
כדי להגדירclassName
ל-''
כדי להחזיר לתצוגה ברירת מחדל - הגדרת
className
של המילה הנוכחית ל-highlight
כדי לסמן אותה כמילה הבאה להקלדה
- הגדרת
- המילה מוקלדת נכון (אך לא הושלמה), מצוין על ידי כך ש-
currentWord
מתחיל ב-typedValue
- הבטחת תצוגת
typedValueElement
כברירת מחדל על ידי ניקויclassName
- הבטחת תצוגת
- אם הגענו עד כאן, יש שגיאה
- הגדרת
className
שלtypedValueElement
ל-error
- הגדרת
בדיקת האפליקציה
הגעתם לסוף! השלב האחרון הוא לוודא שהאפליקציה שלנו עובדת. נסו אותה! אל תדאגו אם יש שגיאות; לכל המפתחים יש שגיאות. בדקו את ההודעות ותקנו לפי הצורך.
לחצו על start, והתחילו להקליד! זה אמור להיראות קצת כמו האנימציה שראינו קודם.
🚀 אתגר
הוסיפו עוד פונקציונליות
- השבתת מאזין אירועי
input
בסיום המשחק, והפעלתו מחדש כאשר לוחצים על הכפתור - השבתת תיבת הטקסט כאשר השחקן משלים את הציטוט
- הצגת תיבת דיאלוג מודאלית עם הודעת ההצלחה
- שמירת תוצאות גבוהות באמצעות localStorage
חידון לאחר ההרצאה
סקירה ולמידה עצמית
קראו על כל האירועים הזמינים למפתח דרך דפדפן האינטרנט, ושקלו את התרחישים שבהם הייתם משתמשים בכל אחד מהם.
משימה
כתב ויתור:
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית Co-op Translator. למרות שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי-דיוקים. המסמך המקורי בשפתו המקורית נחשב למקור הסמכותי. למידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי מתרגם אנושי. איננו נושאים באחריות לכל אי-הבנה או פרשנות שגויה הנובעת משימוש בתרגום זה.