אז ברוכים הבאים לשיעורינו האחרון של קורס תכנות משחקים עם webgl. למעשה סיימנו את הפרויקט דוגמה של הקורס כבר בשיעור הקודם אבל עלה נושא מעניין וחשוב ששווה עוד פוסט קטן. כפי שאנחנו מכירים אובייטקים בתנועה של נפילה או קפיצה בעולם האמיתי, חלה עליהם כח הכבידה , ואם רוצים לקבל תחושת מציאות במשחק או בכל תנועה של אובייקט אתם תרגישו צורך להפעיל כח כבידה כזה או אחר. וכן לפני שניגש לעניין להלן אוסף השיעורים:
להלן רצף השיעורים בקורס תכנות משחקים – webgl
- קורס תכנות משחקים – webgl – שיעור ראשון – הקמת הפרויקט
- קורס תכנות משחקים – webgl – שיעור שני- הסצנה והמצלמה
- קורס תכנות משחקים – webgl – שיעור שלישי- ציור של אובייקט המטבע
- קורס תכנות משחקים – webgl – שיעור רביעי- תנועה של האובייקט הגרפי במרחב
- קורס תכנות משחקים – webgl – שיעור חמישי – עיצוב המטבע ושימוש בתאורה
- קורס תכנות משחקים – webgl – שיעור שישי – לוגיקת סיבוב המטבע וקצת GUI
גרביטציה במרחב תלת מימד בדומה למרחב במציאות
אז יש שני כוחות שפועלים על עצמים פיסיים (יש יותר אבל 2 דיי בסיסיים) והם כח הכבידה (גרביטציה) וחיכוך. שני כוחות אלו חשובים למימוש גם במשחקי תלת מימד על מנת לשקף תנועתיות טבעית , אם המשחק שלכם הוא עצמים בחלל כנראה שפחות תשתמשו בזה אבל ברוב המקרים כן תעשו שימוש במשתנים אלו.
כח הכבידה בסהכ משתנה שיקנה תאוצה, כלומר מהירות משתנה, לאובייקט בכיוון מסויים וכן מהירות שלילית בכיוון ההפוך. חיכוך עושה עבודה דומה רק במצב מסויים למשל עם כדור פוגע ברצפה הוא יאבד מהמהירות שלו ויעלה שוב מעט כלפי מעלה וירד שוב אבל לא יעלה לאותו הגובה שממנו נפל בגלל שקיים חיכוך . וכך הכדור יקפץ עד לעצירה.
עם threejs יש תמיכה שלמה בכל מה שקשור לפיזיקה, זה נושא מאד רחב שדורש קורס בפני עצמו אז לא ניכנס כרגע למימוש עם threejs . יותר חשוב כרגע זה הבנת הרעיון הכללי וקצת פרקטיקה על גבי המשחק שלנו.
בגלל שהמשחק שלנו הוא פשוט ביותר לא חשבתי בהתחלה להוסיף מהירות משתנה אז זה מעיין אלטור של הרגע, ובכל זאת אני חושב ומאמין שזה פרקטי עבורכם כי כבר אנחנו בתוך הקוד ואפשר להפיק עוד קצת תועלת על ידי כך שננסה לגרום למטבע לעלות ולרדת כך שהמהירות בזריקת המטבע תיהיה מהירות ולאט לאט תפחת עד שהמטבע "יעצר באיוור" ואז תתחיל המהירות להתגבר כלפי הצד השני. ועל מנת לעשות זאת הוספתי פרמטר של velocity ובהתאם לקח שהמטבע עולה הקטנתי באופן יחסי את המהירות, ולחילופין בנפילה של המטבע, זה לא הכי בסט פרקטיס אבל זה עושה את העבודה וכן נותן את תחושבת הגרביטציה שרצינו לתת למטבע שלנו. בקורס הבא שאתן אני מאמין שאכנס לדברים ספציפיים יותר לעומק והמימושים יהיו מסודרים הרבה יותר. אבל כפי שכבר אמרתי מספר פעמים זהו קורס למתחילים כך שהדברים מאד בסיסייים ועדיין קשה לייצר משחק בסיסי שישרת היטב את הקורס הזה ולכן השתמשתי בטריקים כאלו ואחרים על מנת להקל על הלמידה. אז מקווה מאד שלמדתם משהו חדש בזכות קורס זה . בהצלחה! 🐊❤
להלן הקוד עם התוספת:
import React, { Component } from 'react'; import * as THREE from 'three'; import side1 from './side1.png'; import side2 from './side2.png'; class Coin extends Component { constructor(props) { super(props); this.state = { textureSide1: new THREE.TextureLoader().load(side1), textureSide2: new THREE.TextureLoader().load(side2) }; this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000); this.scene = new THREE.Scene(); this.renderer = null; this.cylinder = null; this.canvasRef = React.createRef(); } initialObj() { this.camera.position.z = 200; this.renderer = new THREE.WebGLRenderer({ canvas: this.canvasRef.current, alpha: true }); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.setSize(window.innerWidth, window.innerHeight); } componentDidMount() { this.initialObj(); this.addLights(); this.createShape(); } componentDidUpdate(){ if(this.props.play) this.animate(); } createShape() { const geometry = new THREE.CylinderGeometry(10, 10, 1, 100); const material = new THREE.MeshStandardMaterial({ map: this.state.textureSide1, color: 0xFFD700, metalness: 0.3, roughness: 0.3 }) this.cylinder = new THREE.Mesh(geometry, material); this.cylinder.rotation.x = Math.PI / 2; this.cylinder.rotation.y = Math.PI / 2; this.scene.add(this.cylinder); } addLights() { this.scene.add(new THREE.AmbientLight(0xffffff)) // Left point light const pointLightLeft = new THREE.PointLight(0xff4422, 1) pointLightLeft.position.set(-20,-10,10) this.scene.add(pointLightLeft) // Right point light const pointLightRight = new THREE.PointLight(0x44ff88, 1) pointLightRight.position.set(20,10,10) this.scene.add(pointLightRight) // Top point light const pointLightTop = new THREE.PointLight(0xdd3311, 1) pointLightTop.position.set(0,3,2) this.scene.add(pointLightTop); } animate() { let velocity = 2; let frames = 0; let limit = this.props.rand; let spinsCount = 0; const spininngCoin = setInterval(() => { frames++; if (frames % limit === 0) { clearInterval(spininngCoin); this.props.setGame(false); if (spinsCount % 2) { this.cylinder.rotation.x = Math.PI / 2 + Math.PI; this.props.result('bitcoin'); } else { this.cylinder.rotation.x = Math.PI / 2; this.props.result('codcodile'); } } if (frames < limit / 2) { this.camera.position.z -= velocity; velocity = velocity/1.015; } else { this.camera.position.z += velocity; velocity = velocity*1.015; } spinsCount = ~~(this.cylinder.rotation.x / Math.PI); if (spinsCount % 2) { this.cylinder.material.map = this.state.textureSide1; } else { this.cylinder.material.map = this.state.textureSide2; } this.cylinder.rotation.x += 0.025 * Math.PI; this.renderer.render(this.scene, this.camera); }); } render() { return ( <canvas ref={this.canvasRef} className="canvasClass"></canvas> ) } } export default Coin;