This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:tutorials:orxscroll:binding-orxscroll [2021/02/17 09:06 (4 years ago)] – iarwain | en:tutorials:orxscroll:binding-orxscroll [2024/05/06 18:10 (11 months ago)] (current) – Update to match project renaming from binding-of-objects to BindingOfObjects hcarty | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== The Binding of Objects in orx/Scroll ====== | ====== The Binding of Objects in orx/Scroll ====== | ||
- | I keep a github repository | + | The code for this tutorial |
===== What is " | ===== What is " | ||
Line 11: | Line 11: | ||
For instance, you want game objects to do certain things every frame. You want enemies to move on a path, or possibly attack. You want the player' | For instance, you want game objects to do certain things every frame. You want enemies to move on a path, or possibly attack. You want the player' | ||
- | Additionally, | + | Additionally, |
{{: | {{: | ||
- | In this tutorial, we're going to create a small game with two specific examples of object binding. We'll create an Enemy Bug object and a Hero object and bind them to classes. Our enemy bugs will move semi-randomly across the screen. Our Hero will be controlled by the player. | + | In this tutorial, we're going to create a small game with two specific examples of object binding. We'll create an Enemy Bug object and a Hero object and bind them to classes. Our enemy bugs will move semi-randomly across the screen. Our Hero will be controlled by the player. |
First, some preparation... | First, some preparation... | ||
Line 21: | Line 21: | ||
===== Trouble? ===== | ===== Trouble? ===== | ||
- | If you have trouble following this tutorial, please | + | If you have trouble following this tutorial, please |
- | If your problems are related to physics (collision detection), it can be very useful to turn on physics debugging. This is done in .ini config. | + | If your problems are related to physics (collision detection), it can be very useful to turn on physics debugging. This is done in .ini config. |
===== Create a new Scroll Project ===== | ===== Create a new Scroll Project ===== | ||
- | Before you begin this tutorial, you need a basic Orx/Scroll project | + | Before you begin this tutorial, you need a basic Orx/Scroll project |
- | This tutorial assumes you've made a new Orx/ | + | This tutorial assumes you're starting from a fresh '' |
===== Get the Config Ready ===== | ===== Get the Config Ready ===== | ||
Line 38: | Line 38: | ||
{{tutorials: | {{tutorials: | ||
- | Then, you'll need to prepare this config in your main Orx .ini file for use with this tutorial: | + | Then, you'll need to prepare this config in your main project '' |
< | < | ||
- | < | + | < |
- | ; OrxScroll | + | ; BindingOfObjects |
- | ; Should be used with orx v.1.3+ | + | |
- | + | ||
- | [Render] | + | |
- | ShowProfiler | + | |
[Display] | [Display] | ||
- | ScreenWidth | + | ; FullScreen |
- | ScreenHeight | + | Title |
- | Title | + | FullScreen |
- | VSync = true | + | Decoration |
- | Smoothing | + | Smoothing |
- | FullScreen | + | VSync = true |
[Physics] | [Physics] | ||
; Uncomment to show object bounding boxes | ; Uncomment to show object bounding boxes | ||
- | ; ShowDebug = true | + | ; ShowDebug = true |
- | [Input] | + | [Resource] |
- | SetList | + | Texture |
+ | Sound = bundle: # bundle: | ||
[Input] | [Input] | ||
- | KEY_LEFT | + | KEY_ESCAPE |
- | KEY_RIGHT = MoveRight | + | KEY_LEFT |
- | KEY_UP | + | KEY_RIGHT |
- | KEY_DOWN | + | KEY_UP |
+ | KEY_DOWN | ||
[MainViewport] | [MainViewport] | ||
Line 74: | Line 72: | ||
[MainCamera] | [MainCamera] | ||
- | FrustumWidth | + | FrustumWidth |
- | FrustumHeight = @Display.ScreenHeight | + | FrustumHeight |
- | FrustumFar | + | FrustumFar |
- | Position | + | FrustumNear |
+ | Position | ||
+ | |||
+ | [Scene] | ||
+ | ChildList | ||
[O-Hero] | [O-Hero] | ||
- | Graphic | + | Graphic |
- | Position = (512, 384, 0) | + | Position |
- | Body | + | Body = B-Hero |
; Hero Class Data | ; Hero Class Data | ||
- | MovementSpeed = 100.0 | + | MovementSpeed |
[G-Hero] | [G-Hero] | ||
- | Texture = ../data/Character_Boy.png | + | Texture |
[B-Hero] | [B-Hero] | ||
- | PartList = BP-Hero | + | PartList |
- | Dynamic | + | Dynamic |
[BP-Hero] | [BP-Hero] | ||
- | Type = box | + | Type = box |
- | SelfFlags = 0x0001 | + | SelfFlags |
- | CheckMask = 0xFFFF | + | CheckMask |
[O-EnemyBug] | [O-EnemyBug] | ||
- | Position = (0, 200, 0) ~ (1000, 200, 0) | + | Position |
- | Graphic | + | Graphic |
- | Body | + | Body = B-EnemyBug |
; EnemyBug Class Data | ; EnemyBug Class Data | ||
- | MovementSpeed | + | MovementSpeed |
DirectionChangeInterval = 0.5 ~ 2.5 | DirectionChangeInterval = 0.5 ~ 2.5 | ||
[G-EnemyBug] | [G-EnemyBug] | ||
- | Texture = ../data/Enemy_Bug.png | + | Texture |
- | Pivot | + | Pivot |
[B-EnemyBug] | [B-EnemyBug] | ||
- | PartList = BP-Hero | + | PartList |
[BP-EnemyBug] | [BP-EnemyBug] | ||
- | Type = box | + | Type = box |
- | SelfFlags = 0x0002 | + | SelfFlags |
- | CheckMask = 0xFFFF | + | CheckMask |
[FX-Flash] | [FX-Flash] | ||
- | SlotList = FXS-FlashRed # FXS-Unflash | + | SlotList |
[FXS-FlashRed] | [FXS-FlashRed] | ||
- | Type | + | Type = color |
- | Curve = smoother | + | Curve |
- | StartTime | + | StartTime |
- | EndTime | + | EndTime |
- | Absolute | + | Absolute |
- | StartValue = (255, 255, 255) | + | StartValue |
- | Period | + | Period |
- | EndValue | + | EndValue |
[FXS-Unflash] | [FXS-Unflash] | ||
- | Type | + | Type = color |
- | Curve = smoother | + | Curve |
- | StartTime | + | StartTime |
- | EndTime | + | EndTime |
- | Absolute | + | Absolute |
- | StartValue = @FXS-FlashRed.EndValue | + | StartValue |
- | EndValue | + | EndValue |
</ | </ | ||
</ | </ | ||
It's all the typical object, graphic, physics stuff you've seen in previous Orx tutorials. Notice, however, we've added some extra properties to our EnemyBug and Hero objects. The Orx engine doesn' | It's all the typical object, graphic, physics stuff you've seen in previous Orx tutorials. Notice, however, we've added some extra properties to our EnemyBug and Hero objects. The Orx engine doesn' | ||
- | ===== Creating Game Objects in Scroll ===== | ||
- | In Orx, you define | + | A newly created '' |
- | <code c> | + | Run your project. As you might expect, you'll see a hero and a few randomly placed enemy bugs in a gray expanse. Nothing happens since we've defined no behavior to control them. Pressing '' |
- | orxSTATUS OrxScroll:: | + | |
- | { | + | |
- | orxSTATUS result = orxSTATUS_SUCCESS; | + | |
- | | + | Stop and think for a moment about how you might add behavior to the objects in Orx. |
- | { | + | |
- | CreateObject (" | + | |
- | } | + | |
- | return result; | + | |
- | } | + | |
- | </ | + | |
- | Run your project. As you might expect, you'll see a few randomly placed enemy bugs in a gray expanse. Nothing happens since we've defined no behavior to control them. | ||
- | |||
- | Stop and think for a moment about how you might add behavior to the objects in Orx. | ||
===== Giving the Bugs a Brain (Deriving the ScrollObject Class) ===== | ===== Giving the Bugs a Brain (Deriving the ScrollObject Class) ===== | ||
- | The first step to object binding is to create a binding class. To do that, we derive from the ScrollObject base class. | + | The first step to object binding is to create a binding class. To do that, we derive from the '' |
- | First, let's create the interface for our derived class. Create a file called EnemyBug.h and add it to your project. Add the following code to EnemyBug.h: | + | First, let's create the interface for our derived class. Create a file called |
- | <code c> | + | <code c++> |
- | #include " | + | #pragma once |
- | enum Direction { NORTH, SOUTH, EAST, WEST }; | + | #include " |
- | class EnemyBug : public ScrollObject | + | enum Direction |
{ | { | ||
- | private: | + | NORTH, |
- | //! Called on object creation. | + | |
- | | + | EAST, |
- | //! Called on object deletion | + | WEST, |
- | | + | LAST = WEST, |
- | //! Called on clock update | + | }; |
- | virtual void Update (const orxCLOCK_INFO & | + | |
- | | + | class EnemyBug : public Object |
- | Direction | + | { |
- | //! Speed of movement | + | public: |
- | | + | protected: |
- | //! Time since change of direction | + | void OnCreate(); |
- | | + | void OnDelete(); |
- | //! Time interval between direction changes | + | void Update(const orxCLOCK_INFO & |
- | | + | |
+ | private: | ||
+ | | ||
+ | Direction m_direction; | ||
+ | // Speed of movement | ||
+ | | ||
+ | // Time since change of direction | ||
+ | | ||
+ | // Time interval between direction changes | ||
+ | | ||
}; | }; | ||
</ | </ | ||
Line 201: | Line 198: | ||
Let's create the class implementation. Add a file called EnemyBug.cpp to your project and add the following code to it: | Let's create the class implementation. Add a file called EnemyBug.cpp to your project and add the following code to it: | ||
- | <code c> | + | <code c++> |
#include " | #include " | ||
- | void EnemyBug:: | + | void EnemyBug:: |
{ | { | ||
- | | + | |
- | m_direction = SOUTH; | + | m_direction = SOUTH; |
- | // Get movement speed from config value | + | // Get movement speed from config value |
- | m_movementSpeed = orxConfig_GetFloat (" | + | m_movementSpeed = orxConfig_GetFloat(" |
- | // Get direction change interval from config value | + | // Get direction change interval from config value |
- | m_directionChangeInterval = orxConfig_GetFloat (" | + | m_directionChangeInterval = orxConfig_GetFloat(" |
} | } | ||
- | void EnemyBug:: | + | void EnemyBug:: |
{ | { | ||
- | | + | |
} | } | ||
void EnemyBug:: | void EnemyBug:: | ||
{ | { | ||
- | | + | |
- | orxVECTOR speed = orxVECTOR_0; | + | orxVECTOR speed = orxVECTOR_0; |
- | | + | |
- | // current direction of movement. | + | // current direction of movement. |
- | switch (m_direction) | + | switch (m_direction) |
- | { | + | { |
orxBOOL flipX, flipY; | orxBOOL flipX, flipY; | ||
- | | + | |
- | speed.fY = -m_movementSpeed; | + | speed.fY = -m_movementSpeed; |
- | SetRotation (270 * orxMATH_KF_DEG_TO_RAD); | + | SetRotation(270 * orxMATH_KF_DEG_TO_RAD); |
- | SetFlip (false, false); | + | SetFlip(false, |
- | break; | + | break; |
- | case SOUTH: | + | case SOUTH: |
- | speed.fY = m_movementSpeed; | + | speed.fY = m_movementSpeed; |
- | SetRotation (90 * orxMATH_KF_DEG_TO_RAD); | + | SetRotation(90 * orxMATH_KF_DEG_TO_RAD); |
- | SetFlip (false, false); | + | SetFlip(false, |
- | break; | + | break; |
- | case WEST: | + | case WEST: |
- | speed.fX = -m_movementSpeed; | + | speed.fX = -m_movementSpeed; |
- | SetRotation (0 * orxMATH_KF_DEG_TO_RAD); | + | SetRotation(0 * orxMATH_KF_DEG_TO_RAD); |
- | SetFlip (true, false); | + | SetFlip(true, |
- | GetFlip (flipX, flipY); | + | GetFlip(flipX, |
- | break; | + | break; |
- | case EAST: | + | case EAST: |
- | speed.fX = m_movementSpeed; | + | speed.fX = m_movementSpeed; |
- | SetRotation (0); | + | SetRotation(0); |
- | SetFlip (false, false); | + | SetFlip(false, |
- | GetFlip (flipX, flipY); | + | GetFlip(flipX, |
- | break; | + | break; |
- | default: | + | default: |
- | orxASSERT (false); | + | orxASSERT(false); |
- | } | + | } |
- | | + | |
- | SetSpeed (speed); | + | SetSpeed(speed); |
- | | + | |
- | if ((m_timeSinceDirectionChange += _rstInfo.fDT) >= m_directionChangeInterval) | + | if ((m_timeSinceDirectionChange += _rstInfo.fDT) >= m_directionChangeInterval) |
- | { | + | { |
- | // Reset time | + | // Reset time |
- | m_timeSinceDirectionChange = 0; | + | m_timeSinceDirectionChange = 0; |
- | // Pick random number between bounds of Direction enum | + | // Pick random number between bounds of Direction enum |
- | orxU32 randomNum = orxMath_GetRandomU32 (0, highDirection); | + | orxU32 randomNum = orxMath_GetRandomU32(0, |
- | // Update object' | + | // Update object' |
- | m_direction = static_cast< | + | m_direction = static_cast< |
- | } | + | } |
} | } | ||
</ | </ | ||
Line 273: | Line 270: | ||
This is all the code we need to bring our enemy bug to life. The code comments should explain what is happening, but note the following: | This is all the code we need to bring our enemy bug to life. The code comments should explain what is happening, but note the following: | ||
- | * An instance of the EnemyBug class is created for every enemy bug ScrollObject | + | * An instance of the '' |
- | * This class makes use of the SetRotation, | + | * This class makes use of the '' |
- | * OnCreate is called when the object is first created. We didn't define a constructor, | + | * '' |
- | * In OnCreate, we query values in config without pushing the object' | + | * In '' |
* We initialize our class members using the " | * We initialize our class members using the " | ||
- | * OnDelete is called when the object is deleted. We must provide a body for the function, but it does nothing in our case. | + | * '' |
- | * Update is called on every frame. This is the interesting part of EnemyBug. In our case, we update its rotation and speed based on its currently direction of travel. ((Why use SetSpeed and not SetPosition? | + | * '' |
==== ScrollObject Callbacks and Accessors ==== | ==== ScrollObject Callbacks and Accessors ==== | ||
- | * OnCreate, OnDelete, and Update are private | + | * '' |
- | * SetRotation, | + | * '' |
- | If you want to see all the accessors and callbacks available for overriding, see the ScrollObject class interface in the ScrollObject.h file. | + | If you want to see all the accessors and callbacks available for overriding, see the '' |
- | We've now programmed a much more interesting bug. If you run the game, however, you'll still see nothing but still objects. That's because we haven' | + | We've now programmed a much more interesting bug. If you run the game, however, you'll still see nothing but still objects. That's because we haven' |
===== Telling Scroll about the Enemy Bug Class (Overriding BindObjects) ===== | ===== Telling Scroll about the Enemy Bug Class (Overriding BindObjects) ===== | ||
- | The ScrollBindObject function accepts as a template parameter a class deriving from ScrollObject. It accepts as a regular parameter an Orx config section name. Then it binds any instance of the Orx object to the class. | + | The '' |
- | Add the following private function declaration to your OrxScroll class in OrxScroll.h: | + | Include '' |
- | <code c> | + | <code c++> |
- | virtual void BindObjects (); | + | #include " |
</ | </ | ||
- | Now add the function's definition | + | Add the following lines to '' |
<code c> | <code c> | ||
- | void OrxScroll:: | + | // Bind the EnemyBug class to the O-EnemyBug config section |
- | { | + | ScrollBindObject< |
- | | + | |
- | } | + | |
</ | </ | ||
- | The BindObjects function is called when the game starts. It basically says, " | + | The order of calls to '' |
+ | |||
+ | The '' | ||
- | Of course, the EnemyBug class must exist for this to work, which is why we made it first. | + | Of course, the '' |
Run the game and you should see all your enemy bugs come to life. | Run the game and you should see all your enemy bugs come to life. | ||
Line 317: | Line 314: | ||
===== Our Unfortunate Hero (Another Derived ScrollObject) ===== | ===== Our Unfortunate Hero (Another Derived ScrollObject) ===== | ||
- | The bugs in our game must be very hungry! Let's add another bound object. First, add Hero.h to your project and write its interface. | + | The bugs in our game must be very hungry! Let's add another bound object. First, add '' |
- | <code c> | + | <code c++> |
- | #include "OrxScroll.h" | + | #pragma once |
+ | |||
+ | #include "Object.h" | ||
- | class Hero : public | + | class Hero : public |
{ | { | ||
+ | public: | ||
+ | protected: | ||
+ | void OnCreate(); | ||
+ | void OnDelete(); | ||
+ | void Update(const orxCLOCK_INFO & | ||
+ | void OnCollide(ScrollObject *_poCollider, | ||
+ | |||
private: | private: | ||
- | virtual void OnCreate (); | + | |
- | virtual void OnDelete (); | + | |
- | virtual orxBOOL OnCollide (ScrollObject *_poCollider, | + | |
- | const orxSTRING _zPartName, | + | |
- | const orxSTRING _zColliderPartName, | + | |
- | const orxVECTOR & | + | |
- | const orxVECTOR & | + | |
- | virtual void Update (const orxCLOCK_INFO & | + | |
- | + | ||
- | | + | |
}; | }; | ||
</ | </ | ||
- | Hero has a similar interface, but notice we've added an override for OnCollide. This function will be called whenever Orx detects a physics collision between this object and another. | + | Hero has a similar interface, but notice we've added an override for '' |
And add this code: | And add this code: | ||
- | <code c> | + | <code c++> |
#include " | #include " | ||
- | void Hero:: | + | void Hero:: |
{ | { | ||
- | | + | |
- | m_movementSpeed = orxConfig_GetFloat (" | + | m_movementSpeed = orxConfig_GetFloat(" |
} | } | ||
- | void Hero:: | + | void Hero:: |
{ | { | ||
- | | + | |
} | } | ||
void Hero:: | void Hero:: | ||
{ | { | ||
- | | + | |
- | orxVECTOR speed = orxVECTOR_0; | + | orxVECTOR speed = { |
+ | // Vector' | ||
+ | // be 0.0 if the inputs are either inactive or both equally active. | ||
+ | orxInput_GetValue(" | ||
+ | // Vector' | ||
+ | // be 0.0 if the inputs are either inactive or both equally active. | ||
+ | orxInput_GetValue(" | ||
+ | 0.0}; | ||
- | | + | // Normalize the input vector if it has a length > 1 |
- | { | + | |
- | speed.fX = -m_movementSpeed; | + | { |
- | } | + | |
- | else if (orxInput_IsActive (" | + | } |
- | { | + | |
- | speed.fY = -m_movementSpeed; | + | // Scale the raw input vector by the our movement |
- | } | + | |
- | else if (orxInput_IsActive (" | + | |
- | { | + | // Update our speed |
- | speed.fX = m_movementSpeed; | + | SetSpeed(speed, |
- | } | + | |
- | else if (orxInput_IsActive (" | + | |
- | { | + | |
- | speed.fY = m_movementSpeed; | + | |
- | } | + | |
- | | + | |
} | } | ||
- | orxBOOL | + | void Hero:: |
- | const orxSTRING _zPartName, | + | |
- | const orxSTRING _zColliderPartName, | + | |
- | | + | |
- | | + | |
{ | { | ||
- | | + | |
- | AddFX (" | + | AddFX(" |
- | + | ||
- | return true; | + | |
} | } | ||
</ | </ | ||
- | The code should be almost self-explanatory. The hero's movement speed will be pulled from its config value. The update function (called every frame) sets the speed of the character based on what keyboard arrow is pressed. The OnCollide function adds a " | + | The code should be almost self-explanatory. The hero's movement speed will be pulled from its config value. The update function (called every frame) sets the speed of the character based on what keyboard arrow is pressed. The '' |
- | Remember you need to modify the OrxScroll::Init function | + | You have to modify the '' |
- | You also have to modify the OrxScroll:: | + | Try to do those things yourself. If you need help, though, here are the lines to add: |
- | + | ||
- | Try to do those things yourself. If you need help, though, here are the modified functions: | + | |
< | < | ||
<code c> | <code c> | ||
- | orxSTATUS OrxScroll:: | + | // Bind the Hero class to the O-Hero |
- | { | + | ScrollBindObject< |
- | orxSTATUS result = orxSTATUS_SUCCESS; | + | |
- | + | ||
- | CreateObject ("O-Hero"); | + | |
- | + | ||
- | for (orxU32 i = 0; i < 5; i++) | + | |
- | { | + | |
- | CreateObject (" | + | |
- | } | + | |
- | return result; | + | |
- | } | + | |
- | + | ||
- | void OrxScroll:: | + | |
- | { | + | |
- | ScrollBindObject< | + | |
- | | + | |
- | } | + | |
</ | </ | ||
</ | </ | ||
- | When you run the game, you'll be able to control the hero with the arrow keys. Be careful, the bugs will bite him if he gets too close and the OnCollision | + | When you run the game, you'll be able to control the hero with the arrow keys. Be careful, the bugs will bite him if he gets too close and the OnCollide |
{{ : | {{ : | ||
Line 432: | Line 406: | ||
Well, you just finished making what could loosely be considered a game! Here are some additions you could make. | Well, you just finished making what could loosely be considered a game! Here are some additions you could make. | ||
- | * Add a " | + | * Add a " |
- | * Give the Hero a weapon | + | * Give the Hero a weapon |
- | * Be sure to add interesting death animations in the OnDelete callback. | + | * Be sure to add interesting death animations in the '' |
* Add a more interesting background, of course. | * Add a more interesting background, of course. |