• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.

Archived

This topic is now archived and is closed to further replies.

WitchLord

AngelScript 1.8.0 WIP #2 (2004/06/26)

47 posts in this topic

That's quite a lot of questions. Let's see if I can satisfy your curiousity. By the way, have you read the overview that I recently wrote? http://www.angelcode.com/angelscript/articles/overview.html

asBSTR is perhaps not the most convenient way of working with strings when it comes to C++. You're better of using std::string or perhaps even acCString (that you'll find in the AngelScript library source). Here's an example on how you would use asBSTR in C++:


#include "bstr.h"

void TestBStr()
{
// Allocate a string with 10 characters
asBSTR aString = asBStrAlloc(10);

// Copy data to the string buffer
memcpy(aString, "Hello there", asBStrLength(aString));

// asBStr guarantees a null character after the allocated length
printf("%s\n", aString);

// Free the allocated memory
asBStrFree(aString);
}


This code would output


Hello ther


Exactly 10 characters as the bstr puts a null character after the allocated length.

-----

You should register all the methods of CGame object you wish to give the scripts access to. You may also register member variables for direct manipulation. The member variables could be registered as const so that the script cannot alter the value.

Once you've registered the interface to CGame you could register a global property with either a reference to the CGame object or a pointer. Or you could register a function that returns the reference or pointer to the object.

Whatever you choose you can be sure that the script will not be able to do anything with the object that you haven't allowed it to do. That is, the script cannot change any internal states of the CGame except through the registered functions/methods or properties.

----

I'm happy you like AngelScript. I started developing AngelScript for the same reason you started using it. I took a look at Lua and Python but didn't like what I saw, and thought that I could do it better myself. Perhaps not better, but more to my liking. As it turns out a lot of other people also think it is easier to use, which makes me happy with my decision. :)

----

There is no fixed entry point for the scripts. It is the application that decides what it needs. You could for example want to write an application that runs one script function and lets the script control the rest of the program flow. In that case it makes sense to call the script function "main". But other situations may need an initialization function and a termination function and nothing between. In those cases the functions might be called "init" and "uninit". It's all up to you. There is no "fits-all" solution.

The engine compiles the script pretty fast. The exact time depends on size of the script of course. But you should try to compile the scripts once, and then call the compiled functions.

Should you so many scripts that you are having trouble with memory, you might need to rethink the design of your game. Perhaps it would be possible divide the game in sections each with a smaller amount of scripts. When the player moves from one section to another the respective scripts are loaded and compiled.

-----

Scripts are compiled into bytecode and executed sequentially in separate contexts. When you call Execute() the function doesn't return until the script execution finishes or is suspended. If you have a controlling script that runs in an endless loop, you might need to suspend the script in order to allow other tasks to run once in a while. A suspended script can be resumed by calling Execute() again.

You may also want to allow AI to execute a certain number of instructions per frame. You could do that by calling ExecuteStep() the number of times desired per object and per frame. ExecuteStep() executes one script statement then returns. This way you could have several script contexts running in parallel, effectively making a multitasking scripting engine.

----

That code is a good example on how to load a script, compile it and execute it. You do well to learn from it. But again, it might not suit your purpose so don't follow it blindly.

----

Regards,
Andreas
0

Share this post


Link to post
Share on other sites
Great answer. I just want to tell you the basic idea of what I want to do, and maybe you could point me in the right direction.

My game will be an arcade game. It will contain lots of enemies that each have scripts that are executed when they die. I would like to lump all the enemy scripts together in one script (seperated by functions maybe). Then each enemy would have a string containing the name of the function to call when it dies ("sharkDie()"). Then I would call something to execute "sharkDie()" using the compiled script. I would also use the scripting for loading/configuration, and maybe a menu system.

Read the overview. The first part about compiling scripts: should I just compile all my scripts together using AddScriptSection? Then call each function by using the method on the overview? So I gather there is no way to have different compiled scripts that work independantly from one another?
0

Share this post


Link to post
Share on other sites
My recommendation is that you have one engine for each type of task, for example: One for AI routines, one for GUI management, one for loading/configuration.

The AI routines should be divided in groups depending on type of object. You could for example have a soldier type and a shark type, both of them have the same interface to interact with the game engine so they belong in the same script engine. Both types have the same script interface so your application needs a way to differentiate between the die function for the soldier and the shark. In version 1.7.1a the recommended way was to append the name of the type to each of the functions giving soldierDie() and shartDie(). But with 1.8.0 I recommend that you use the modules instead which gives soldier::Die() and shark::Die(). In 1.7.1a you would compile all the AI scripts together by first adding all the script sections then calling Build(), but in 1.8.0 you compile each module separately.

0

Share this post


Link to post
Share on other sites
with 1.8.0 I recommend that you use the modules instead which gives soldier::Die() and shark::Die().

How do I use the modules in the scripting language? like
soldier::Die()
{
}

just like that?

And, in the host program, what do you mean compile each module seperately? I wouldn't compile a whole script (by script I mean a complete textfile containing various functions), but just parts of a script? Why not just compile the whole script and call individual functions from the script? But, if I have to or should use modules, the enemies die script function would be something like: "soldier::die()"?
0

Share this post


Link to post
Share on other sites
Modules is a new feature in version 1.8.0 WIP #3 (released yesterday).


Say that you have two script files. One for the soldier AI and
one for the shark AI.




// Code for soldier AI

void Die()
{
// Do the soldier death animation
}




// Code for shark AI

void Die()
{
// Do the shark death animation
}




Now load these scripts into the engine and compile them.


soldierScript = LoadScript("soldier.as");
engine->AddScriptSection("soldier", soldierScript, strlen(soldierScript);
engine->Build("soldier");

sharkScript = LoadScript("shark.as");
engine->AddScriptSection("shark", sharkScript, strlen(sharkScript);


Once you've loaded and compiled the scripts. You can get the id's of the functions like so:


soldierDieFunc = engine->GetFunctionIDByName("soldier", "Die");
sharkDieFunc = engine->GetFunctionIDByName("shark", "Die");


The modules just makes it easier to manage all your AI types. And you could also load script modules dynamically as the player encounters new AI types.

Of course, you are not prohibited from compiling everything into one large script, should it suit you better.
0

Share this post


Link to post
Share on other sites
I get what you are saying but I don't get how to implement it.

the soldier will have a function ID to tell what happens when he dies. Since it is a soldier, the module to be used is assumed to be "PEOPLE" or something. So the function ID could be obtained like so:

acCString str; str.Allocate(256, false);
soldier->ID = engine->GetFunctionDeclaration(engine->GetFunctionIDByName(mod, "main"), str.AddressOf(), 256);

To load and get the script file ready to be accessed by the scripting engie I would call LoadScript? Okay, here is my loadscript code:

int LoadandCompile ( asIScriptEngine *engine, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
int c = fread(code.AddressOf(), len, 1, f);
fclose(f);
int r = engine->AddScriptSection(mod, code, len);
COutStream out; r = engine->Build(mod, &out);
}

but AddScriptSection does not take 3 parameters.

Then to call the soldiers die function you call
int ExecuteFunction ( asIScriptEngine *engine, const char *mode, int function )
{
asIScriptContext *ctx;
int r = engine->CreateContext(&ctx);
r = ctx->Prepare(function);
r = ctx->Execute();
return 0;
}

but, Prepare does not take 1 parameter.

I guess that would be the way to do it, right? What is the most efficient way? Obviously writing all that code to load a script and set functions and whatnot is a lot of writing. That is why I think making functions like the ones above would help, but I don't think they are right. I admit that I don't understand how to use as, but I am kinda picking up on it.



0

Share this post


Link to post
Share on other sites
Ok, I can't tell you how exactly you should implement things. Everyone have their own preferred solution. Not even I know what is best since I don't use AngelScript. I don't have the time to write any projects using AngelScript.

The problem about the wrong number of arguments is probably because you haven't downloaded the latest 1.8.0 WIP #3. Or you haven't updated the angelscript.h file in your project.

Your function for loading and compiling the script is fine. If you want to compile more than one script section into the same module you should only call Build() after you have added all the sections.

GetFunctionDeclaration() is only there so that you can get the function interface declared by the script. You don't need to store this string. To get the function ID you ought to use GetFunctionIDByDecl() since it guarantees that the function you're calling is using the interface that you expect.

int funcID = engine->GetFunctionIDByDecl("mymodule", "void DoSomething(float)");

This call is relatively slow so it is recommended that you store the funcID for later use.

To call a script function you need to prepare a context. Then call Execute() on it.

asIScriptContext *ctx;
engine->CreateContext(&ctx);
ctx->Prepare(funcID, 100); // Can't remember right now if there are more parameters, check angelscript.h or the app writer's manual
float arg = 1.0;
ctx->SetArguments(0, &arg, 1);
ctx->Execute();
ctx->Release();

You could optimize it some more by reusing the context for another execution. Just call Prepare() again, and then Execute().

Regards,
Andreas
0

Share this post


Link to post
Share on other sites
I can't thank you enough for being one of the most patient guys I've ever communicated with. This whole thread is pretty informative for anyone trying to learn angelscript.
0

Share this post


Link to post
Share on other sites
Problem when calling this function. The thing it craps out on is the r = engine->AddScriptSection(); What is the difference between mod and name, and what should I put for len? I downloaded the sources just now, and it didn't change anything for the parameters.

int LoadandCompile ( asIScriptEngine *engine, asIScriptContext *ctx, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
int c = fread(code.AddressOf(), len, 1, f);
fclose(f);
hge->System_Log("%d size adding script section..", len);
int r = engine->AddScriptSection(mod, "flatch", code, len);
//COutStream out;
hge->System_Log("building engine");
r = engine->Build(mod, NULL);
hge->System_Log("creating context");
r = engine->CreateContext(&ctx);
if( r System_Log("Failed to create a context"); return -1; }
return 0;
}
0

Share this post


Link to post
Share on other sites
You're most welcome :)

The module parameter is the name of the module, which basically is the namespace for the script section.

The name parameter is the name of the script section. This name will only be used during the build for writing error messages. A good choice for this parameter is the filename of the script.

I can't see any obvious errors in your code. Does AddScriptSection() return a value, or does the application crash? Is the crash an assert failure, or something else?

If you receive a return value your parameters are probably wrong somehow.

If you receive an assert failure, it could mean a bug in AngelScript.

If you application crashes for other reasons my guess is that somehow you are linking to the wrong version of AS. Remember that differences between WIP versions are not detected by asCreateEngine() as the version number is the same.

- Andreas
0

Share this post


Link to post
Share on other sites
I have downloaded and completely reinstalled the newest WIP of angelscript. The file I downloaded was angelscript_sdk_180WIP3.zip
I also completely removed any other version of the lib, but still the parameters from what you say it should be and the parameters I have for some functions are different. I rebuilt the lib and everything (msvc) so I don't know what's wrong. Maybe you didn't update the msvc workspace or something? Maybe I need to do something to my project?
0

Share this post


Link to post
Share on other sites
just using the version I have, here is what I am having trouble with. When I try the GetFunctionIDbyDecl() I get different error codes. The last I tried I got -1 which is just error.
When the program gets to the line ctx->Prepare(f, 1000); it crashes (in debug mode) and points to that line of code. You said the crashing may be a result of version conflicts, but I have downloaded the most recent WIP.
Here is my code and how it basically is in my program:

/* script file */
void main()
{
SND_PWRUP = 5;
return 0;
}

/* Loading */

engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
ConfigureEngine(engine);
ctx = NULL;
LoadandCompile( engine, ctx, "flatch.as", "" );

/* Load and compile */

int LoadandCompile ( asIScriptEngine *engine, asIScriptContext *ctx, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
int c = fread(code.AddressOf(), len, 1, f);
fclose(f);
int r = engine->AddScriptSection(mod, filename, code, len);
//COutStream out;
r = engine->Build(mod, NULL);
r = engine->CreateContext(&ctx);
context"); return -1; }
return 0;
}

/* execute the script */

int f = engine->GetFunctionIDByDecl("", "void main()");
int r = ctx->Prepare(f, 1000); assert ( r > 0);
ctx->Execute();
0

Share this post


Link to post
Share on other sites
I wont be using WIP#3 for now(will wait for the full release), but i wonder if adding different script sections would help debugging, i.e, if there is an error on line 3 col 45 of the script file, would the error be poitted out relative to the file or to the section.

Wonder how many changes i would have to make for 1.8.0 from 1.7.1 :)
0

Share this post


Link to post
Share on other sites
EddHead:

Since AngelScript doesn't know anything about files it reports errors relative to the start of script sections.

I will do my best to make the upgrade to 1.8.0 as smooth as possible.

Anonymous Poster:

I can't see any obvious errors in your code. Could you tell me what the Build() method returned? And what GetFunctionIDByDecl() returned?

I'm not ruling out a bug in AngelScript so I'll be making some tests here, but you can help me by answering the above questions.
0

Share this post


Link to post
Share on other sites
I got a login now.

build returned 0. Create context returned 0. GetFunctionIDbyDecl returned -1.

If you want I can send you the full source and workspace. I probably am doing something wrong.

Here is where I try to call a function in the script ctx:

if (ctx) {

int function = engine->GetFunctionIDByDecl("", "void main(void)");
hge->System_Log("function ID for main: %d.. Ctx: %d", function, ctx);
if ( function > -1 )
{
int r = ctx->Prepare(function, 1000);
hge->System_Log("prepare %d = %d", function, r);
hge->System_Log("SND_PWRUP: %d", SND_PWRUP);
ctx->Execute();
hge->System_Log("SND_PWRUP: %d", SND_PWRUP);
}
}
0

Share this post


Link to post
Share on other sites
Ped Xing:

Welcome aboard. It's good to have a name to refer to, instead of just Anonymous Poster :)

Ok, in the last source you sent me I saw the error. You are searching for a function by "void main(void)". It's a syntax error to use void for parameters. Try "void main()" instead and I think you'll get a valid function ID.

The program crash you were getting was because you were calling Prepare() with the negative function ID. That was a bug in AngelScript that I've found and fixed. The fix will be in the next WIP version.

Regards,
Andreas
0

Share this post


Link to post
Share on other sites
int function = engine->GetFunctionIDByDecl("", "void main()");

this call still makes function -1. Are there any headers or anything I need to put in the script file?
0

Share this post


Link to post
Share on other sites
Strange.

Would you mind downloading WIP #4, so that we are working on the same version? I released this version about an hour ago.

I fixed a bug in the Build() method, that may be related to your problem.

I just noticed another problem in your script file. You've declared the script function as "void main()" yet your implementation returns 0. This is an error and makes the Build() fail. The bug I fixed is that the Build() function could at times report success even though it failed.

I suggest you add an output stream when calling the Build() function, that would allow you to catch any errors in the script.


class COutStream : public asIOutputStream
{
public:
void AS_CALL Write(const char *text) { printf(text); }
};

COutStream out;
r = engine->Build(module, &out);
if( r < 0 )
{
// failure
}
0

Share this post


Link to post
Share on other sites
dude, it works! You're right, I put return 0; when it should have been return. More problems though. When I add in the COutStream code my program gives me this error:

Quote:

error LNK2001: unresolved external symbol "public: virtual void __stdcall COutStream::Write(char const *)" (?Write@COutStream@@UAGXPBD@Z)


0

Share this post


Link to post
Share on other sites
The linker can't find your implementation of the COutStream::Write() method. Try putting the implementation outside the class declaration.

0

Share this post


Link to post
Share on other sites
also, I am still a little confused about letting the scripting engine directly acces objects in my game.
like if I have a struct

struct point
{
int x, y;
};

point *p = new (point);

r = engine->RegisterObjectType ("point", 0, 0);
r = engine->RegisterGlobalPropery ("p", p);

how can I get the engine to use the actual instance of an object than just a class? I really don't know how to explain it or what I am talking about (need to work on my vocabulary).

0

Share this post


Link to post
Share on other sites
I understand that it's all relative to the script. perhaps each section should be in a seperate file.

Thanks
0

Share this post


Link to post
Share on other sites
EddHead:

That would be the easiest way to solve it.

Ped Xing:

You're almost doing it correctly already.


struct point
{
int x, y;
};

point *p = new point;

// stddef.h declares the offsetof() macro
#include <stddef.h>

// Register the type
r = engine->RegisterObjectType ("point", sizeof(point), 0);
r = engine->RegisterObjectProperty("point", "int x", offsetof(point, x));
r = engine->RegisterObjectProperty("point", "int y", offsetof(point, y));

// Register a global property that can be accessed normally
r = engine->RegisterGlobalPropery("point p", p);


When you register a property you should send a pointer to the property as the second argument. In this case I'm registering a global point, and send a pointer to a point.

You can also register the properties as const so that the scripts can't change the values. Just put const in the declaration, just like in C++.
0

Share this post


Link to post
Share on other sites