Advertisement

Passing a value into a bound class.

Started by September 29, 2012 04:21 AM
21 comments, last by TechRogue 12 years, 2 months ago

[quote name='Jake Albano' timestamp='1348947598' post='4985130']
Here's how I give my classes access to the main engine class:

When I set up the script engine, I pass the address of my engine class instance in as userdata (a void pointer):


engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetUserData(&myGameEngine);


Then, in my class constructors that need a pointer to the engine (to access window drawing, content management, etc.), I do this:


asIScriptContext* context = NULL;
asIScriptEngine* engine = NULL;
context = asGetActiveContext(); // Get the currently executing angelscript context
if ((context = asGetActiveContext()) != NULL && (engine = context->GetEngine()) != NULL)
{
// owner is a member pointer to the engine
owner = reinterpret_cast<Engine*>(engine->GetUserData());
}


Hopefully that helps.


Gonna try that but here is my engine class


#include "Engine.h"
#include <windows.h> // For the WaitMessage() function.
#include "Scripting/ScriptManager.h"
using namespace Ogre;
/** Default constructor. **/
AffinityEngine::AffinityEngine() {
m_lLastTick = 0;
m_iWidth = 1024;
m_iHeight = 768;
m_czTitle = 0;
m_pScreen = 0;
m_iFPSTickCounter = 0;
m_iFPSCounter = 0;
m_iCurrentFPS = 0;
m_bMinimized = false;
}
/** Destructor. **/
AffinityEngine::~AffinityEngine() {
SDL_Quit();
}
/** Sets the height and width of the window.
@param iWidth The width of the window
@param iHeight The height of the window
**/
void AffinityEngine::SetSize(const int& iWidth, const int& iHeight) {
m_iWidth = iWidth;
m_iHeight = iHeight;
if(m_vmVideoMode == VideoMode_3D) {
m_pScreen = SDL_SetVideoMode( iWidth, iHeight, 0, SDL_OPENGL );
} else if(m_vmVideoMode == VideoMode_2D) {
m_pScreen = SDL_SetVideoMode( iWidth, iHeight, 0, SDL_SWSURFACE );
}
}
/** Initialize SDL, the window and the additional data. **/
void AffinityEngine::Init(VideoMode videoMode) {
printf("Engine Initialing.\n");
// Register SDL_Quit to be called at exit; makes sure things are cleaned up when we quit.
atexit( SDL_Quit );
// Initialize SDL's subsystems - in this case, only video.
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
fprintf( stderr, "Unable to init SDL: %s\n", SDL_GetError() );
exit( 1 );
}
SetTitle("Affinity Engine");
m_vmVideoMode = videoMode;
// Attempt to create a window with the specified height and width.
SetSize(m_iWidth, m_iHeight);
if(videoMode == VideoMode_3D){
m_rRoot = new Root("plugins_d.cfg", "ogre.cfg", "AffinityEngine.log");
m_rRoot->restoreConfig();
m_rRoot->initialise(false);
NameValuePairList misc;
#ifdef _WIN32
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWMInfo(&wmInfo);

size_t winHandle = reinterpret_cast<size_t>(wmInfo.window);
size_t winGlContext = reinterpret_cast<size_t>(wmInfo.hglrc);

misc["externalWindowHandle"] = StringConverter::toString(winHandle);
misc["externalGLContext"] = StringConverter::toString(winGlContext);
#else
misc["currentGLContext"] = String("True");
#endif
m_rwRenderWindow = m_rRoot->createRenderWindow("MainRenderWindow", 1024, 768, false, &misc);
m_rwRenderWindow->setVisible(true);
printf("Initialized 3D Rendering.\n");
} else {
printf("Initialized 2D Rendering.\n");
}
// If we fail, return error.
if ( m_pScreen == NULL ) {
fprintf( stderr, "Unable to set up video: %s\n", SDL_GetError() );
exit( 1 );
}
printf("Engine Initialized.\n");
AdditionalInit();
}
/** The main loop. **/
void AffinityEngine::Start() {
printf("Engine Started\n");
m_lLastTick = SDL_GetTicks();
m_bQuit = false;
m_smScriptManager = new ScriptManager("../Data/Scripts", this);
m_smScriptManager->Init();
// Main loop: loop forever.
while ( !m_bQuit ) {
// Handle mouse and keyboard input
HandleInput();
if ( m_bMinimized ) {
// Release some system resources if the app. is minimized.
WaitMessage(); // pause the application until focus in regained
} else {
// Do some thinking
DoLogic();
// Render stuff
DoRender();
}
}
End();
}
/** Handles all controller inputs.
@remark This function is called once per frame.
**/
void AffinityEngine::HandleInput() {
// Poll for events, and handle the ones we care about.
SDL_Event event;
while ( SDL_PollEvent(&event) ) {
switch (event.type) {
case SDL_KEYDOWN:
// If escape is pressed set the Quit-flag
/*if (event.key.keysym.sym == SDLK_ESCAPE){
m_bQuit = true;
break;
}*/
m_smScriptManager->e_imInputMgr->InjectKeyboard(event.key.keysym.sym, InputState::Down);
KeyDown(event.key.keysym.sym);
break;
case SDL_KEYUP:
m_smScriptManager->e_imInputMgr->InjectKeyboard(event.key.keysym.sym, InputState::Up);
KeyUp(event.key.keysym.sym);
break;
case SDL_QUIT:
m_bQuit = true;
break;
case SDL_MOUSEMOTION:
m_smScriptManager->e_imInputMgr->InjectMouse(event.motion.x, event.motion.y, event.button.button);
MouseMoved(event.button.button, event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel);
break;
case SDL_MOUSEBUTTONUP:
m_smScriptManager->e_imInputMgr->InjectMouse(event.motion.x, event.motion.y, event.button.button);
MouseButtonUp(event.button.button, event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel);
break;
case SDL_MOUSEBUTTONDOWN:
m_smScriptManager->e_imInputMgr->InjectMouse(event.motion.x, event.motion.y, event.button.button);
MouseButtonDown(event.button.button, event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel);
break;
case SDL_ACTIVEEVENT:
if ( event.active.state & SDL_APPACTIVE ) {
if ( event.active.gain ) {
m_bMinimized = false;
WindowActive();
} else {
m_bMinimized = true;
WindowInactive();
}
}
break;
} // switch
} // while (handling input)
}
/** Handles the updating routine. **/
void AffinityEngine::DoLogic() {
long iElapsedTicks = SDL_GetTicks() - m_lLastTick;
m_lLastTick = SDL_GetTicks();
m_smScriptManager->ParseScripts();
Logic( iElapsedTicks );
m_smScriptManager->Update();
m_iFPSTickCounter += iElapsedTicks;
}
/** Handles the rendering and FPS calculations. **/
void AffinityEngine::DoRender() {
++m_iFPSCounter;
if ( m_iFPSTickCounter >= 1000 ) {
m_iCurrentFPS = m_iFPSCounter;
m_iFPSCounter = 0;
m_iFPSTickCounter = 0;
}
if(m_vmVideoMode == VideoMode_2D) {
SDL_FillRect( m_pScreen, 0, SDL_MapRGB( m_pScreen->format, 0, 0, 0 ) );
// Lock surface if needed
if ( SDL_MUSTLOCK( m_pScreen ) )
if ( SDL_LockSurface( m_pScreen ) < 0 )
return;
Render( GetSurface() );
m_smScriptManager->Draw();
// Unlock if needed
if ( SDL_MUSTLOCK( m_pScreen ) )
SDL_UnlockSurface( m_pScreen );
// Tell SDL to update the whole gScreen
SDL_Flip( m_pScreen );
}else{
m_rRoot->renderOneFrame();
SDL_GL_SwapBuffers();
}
}
/** Sets the title of the window
@param czTitle A character array that contains the text that the window title should be set to.
**/
void AffinityEngine::SetTitle(std::string czTitle) {
m_czTitle = czTitle.c_str();
SDL_WM_SetCaption( czTitle.c_str(), 0 );
}
/** Retrieve the title of the application window.
@return The last set windows title as a character array.
@remark Only the last set title is returned. If another application has changed the window title, then that title won't be returned.
**/
const char* AffinityEngine::GetTitle() {
return m_czTitle;
}
/** Retrieve the main screen surface.
@return A pointer to the SDL_Surface surface
@remark The surface is not validated internally.
**/
SDL_Surface* AffinityEngine::GetSurface() {
return m_pScreen;
}
/** Get the current FPS.
@return The number of drawn frames in the last second.
@remark The FPS is only updated once each second.
**/
int AffinityEngine::GetFPS() {
return m_iCurrentFPS;
}
void AffinityEngine::Shutdown() {
m_bQuit = true;
}
ScriptManager* AffinityEngine::GetScriptManager() {
return m_smScriptManager;
}


Ok while making those changes I got this error:

Engine Initialing.
Initialized 2D Rendering.
Engine Initialized.
Engine Started
(0, 0) : ERR : Failed in call to function 'RegisterObjectBehaviour' with 'Imag
e' and 'ref@ f()' (Code: -10)


from this line


r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "ref@ f()", asFUNCTION(Img_Factory), asCALL_CDECL); assert(r >= 0);


and here is the function:


static Img* Img_Factory() {
return new Img();
}

[/quote]

Once I get my game running again I can test to see if the changes worked. (Sorry for the double post meant to edit >.<)
You'll want to do it like this:


r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "Image@ f()", asFUNCTION(Img_Factory), asCALL_CDECL); assert(r >= 0);


Using "image" instead of "ref".
Advertisement
i might be missing the point here but wouldn't singleton engine class solve this?


class Engine
{
private:
Engine::Engine();
static Engine *instance;
public:
static Engine *get()
{
if (!instance)
instance = new Engine;
return instance;
}
void init()
{
// Do init.
}
}
Engine* Engine::instance = 0;


they you will have access to it anywhere anytime.
I avoid singletons since they're essentially global variables, but yes, that would be one way to solve the problem.
a very wrong misconception.
singletons are definitely not global variables. you cant control access with global variables.

the way you do it is more akin to a global variable.
it makes engine to accessible and volatile to every piece of code in your program.

what if some evil soul decides to call asGetActiveContext()->GetEngine()->SetUserdata(null);
angry employee maybe?
I strongly suggest you read this article: http://gameprogrammi.../singleton.html Singletons have a lot of attractive features at first glance, but they're really a bad idea in the long run.

I do understand the difference between a plain global and the singleton pattern, but functionally they serve the same purpose of providing global access to the same variable anywhere in your program. Additionally, my engine is designed so I could have multiple instances of it running in the same program. Using a singleton for the engine would prevent me from doing this.

As far as malicious use goes (although I'm not sure why that would ever be a concern), your singleton example above is just as susceptible:


delete Engine::get();


To each his own. Let's not go any further off topic. :)
Advertisement

i might be missing the point here but wouldn't singleton engine class solve this?


class Engine
{
private:
Engine::Engine();
static Engine *instance;
public:
static Engine *get()
{
if (!instance)
instance = new Engine;
return instance;
}
void init()
{
// Do init.
}
}
Engine* Engine::instance = 0;


they you will have access to it anywhere anytime.


Ok the singleton thing to me is a relatively new concept I can do it in C# but C++ eludes me.

Here is the game class that inherits from the engine class and I added your singleton code to it:


#ifndef GAME_H
#define GAME_H
#include "Engine\Engine.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <string>
#include <iostream>
/*
#ifdef _MSC_VER
#include <crtdbg.h> // debugging routines
#endif
*/
using namespace std;

class Game : public AffinityEngine {
private:
static Game *instance;
public:
Game();
~Game();
void AdditionalInit ();
void Logic (const int& iElapsedTime );
void Render (SDL_Surface* pDestSurface );

void KeyUp (const int& iKeyEnum);
void KeyDown (const int& iKeyEnum);

void MouseMoved (const int& iButton, const int& iX, const int& iY, const int& iRelX, const int& iRelY);

void MouseButtonUp (const int& iButton, const int& iX, const int& iY, const int& iRelX, const int& iRelY);

void MouseButtonDown(const int& iButton, const int& iX, const int& iY, const int& iRelX, const int& iRelY);

void WindowInactive ();
void WindowActive ();

void End ();
void Setup ();
static Game *get() {
if (!instance){
instance = new Game;
}
return instance;
}
};
Game::Game() {
instance = this;
}
Game::~Game() {
//delete m_smScriptManager;
}
void Game::Setup() {
}
#endif


when I compile I get this:


1>ScriptManager.obj : error LNK2005: "public: void __thiscall Game::Setup(void)" (?Setup@Game@@QAEXXZ) already defined in Engine.obj
1>ScriptManager.obj : error LNK2005: "public: virtual __thiscall Game::~Game(void)" (??1Game@@UAE@XZ) already defined in Engine.obj
1>ScriptManager.obj : error LNK2005: "public: __thiscall Game::Game(void)" (??0Game@@QAE@XZ) already defined in Engine.obj
1>Img.obj : error LNK2005: "public: void __thiscall Game::Setup(void)" (?Setup@Game@@QAEXXZ) already defined in Engine.obj
1>Img.obj : error LNK2005: "public: virtual __thiscall Game::~Game(void)" (??1Game@@UAE@XZ) already defined in Engine.obj
1>Img.obj : error LNK2005: "public: __thiscall Game::Game(void)" (??0Game@@QAE@XZ) already defined in Engine.obj
1>main.obj : error LNK2005: "public: void __thiscall Game::Setup(void)" (?Setup@Game@@QAEXXZ) already defined in Engine.obj
1>main.obj : error LNK2005: "public: virtual __thiscall Game::~Game(void)" (??1Game@@UAE@XZ) already defined in Engine.obj
1>main.obj : error LNK2005: "public: __thiscall Game::Game(void)" (??0Game@@QAE@XZ) already defined in Engine.obj
1> Creating library C:\Users\Xios\Documents\Visual Studio 2010\Projects\AffinityEngine-Oblivion\Debug\AffinityEngine-Oblivion.lib and object C:\Users\Xios\Documents\Visual Studio 2010\Projects\AffinityEngine-Oblivion\Debug\AffinityEngine-Oblivion.exp
1>MSVCRTD.lib(cinitexe.obj) : warning LNK4098: defaultlib 'msvcrt.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
1>Engine.obj : error LNK2001: unresolved external symbol "private: static class Game * Game::instance" (?instance@Game@@0PAV1@A)


turns out it was because I had a couple of my functions implemented in the .h file now I only have:


Game.obj : error LNK2001: unresolved external symbol "private: static class Game * Game::instance" (?instance@Game@@0PAV1@A)


That was fixed by making a Game.cpp and putting this in it.


Game* Game::instance = 0;
Game::Game() {
Game::instance = this;
}


just helping others with the same issue later.

Now I don't get any errors from the program or the script but my image doesn't show up. If I had to wager a guess it's that the image class still is not getting the right engine instance regardless of the singleton. Is my script running on a separate thread? Is that the issue because I have not specifically tried. I would have to assume this is correct because when I try this:


image = new Img();
image->X = 0;
image->Y = 0;
image->fileName = "AyphixPresents.png";


inside of C++ it works great. Which is essentially the same thing my script does when I do this:


class GameManager {
string GameTitle;
bool quit;
Image@ img;
GameManager(string gameTitle) {
quit = false;
GameTitle = GameTitle;
Application.SetTitle(gameTitle);
@img = Image();
img.X = 0;
img.Y = 0;
img.Filename = "../Data/Images/AyphixPresents.png";
}
void Update() {
if(Input.GetKeyDown(Keycode::Escape)){
Application.Exit();
}
//img.draw();
}
}



Problem solved :)


r = e_seScriptEngine->RegisterObjectProperty("Image", "int Y", asOFFSET(Img, Y)); assert(r >= 0);

asOFFSET(Img, Y) was asOFFSET(Img, X) when I changed it, it worked. Thanks to both of you for the help!
Ok one last question before I call this success :)

I want to be able to pass my x y and filename into my constructor;

But my factory looks like this with no clue what angelscript is passing into the constructor.


static Img* Img_Factory() {
return new Img();
}


Does it really matter what my factory has? Will I will be able to register a constructor with those in it?
Yes, this is possible. You can register multiple constructors for a class:

You'll need separate factory functions:

static Img* Img_Factory() {
return new Img();
}

static Img* Img_Factory(int x, int y) {
return new Img(int x, int y);
}


And register them this way:

r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "Image@ f()", asFUNCTION(Img_Factory), asCALL_CDECL); assert(r >= 0);
r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "Image@ f(int, int)", asFUNCTIONPR(Img_Factory, (int, int), Img*), asCALL_CDECL); assert(r >= 0);


asFUNCTIONPR lets you distinguish between overloaded functions.
http://www.angelcode.com/angelscript/sdk/docs/manual/doc_register_func.html

Yes, this is possible. You can register multiple constructors for a class:

You'll need separate factory functions:

static Img* Img_Factory() {
return new Img();
}

static Img* Img_Factory(int x, int y) {
return new Img(int x, int y);
}


And register them this way:

r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "Image@ f()", asFUNCTION(Img_Factory), asCALL_CDECL); assert(r >= 0);
r = e_seScriptEngine->RegisterObjectBehaviour("Image", asBEHAVE_FACTORY, "Image@ f(int, int)", asFUNCTIONPR(Img_Factory, (int, int), Img*), asCALL_CDECL); assert(r >= 0);


asFUNCTIONPR lets you distinguish between overloaded functions.
http://www.angelcode...ister_func.html


:) Thanks a ton for all the help.

This topic is closed to new replies.

Advertisement