Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411713 Posts in 69402 Topics- by 58450 Members - Latest Member: FezzikTheGiant

May 21, 2024, 08:23:01 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsVatnsmyrkr【submarine exploration】
Pages: 1 ... 7 8 [9] 10 11 ... 18
Print
Author Topic: Vatnsmyrkr【submarine exploration】  (Read 43112 times)
oldblood
Level 10
*****

...Not again.


View Profile
« Reply #160 on: May 26, 2015, 04:23:35 AM »

Wow, thats awesome. Nicely done man! There aren't a lot of 2D games where I feel like normal maps would do much for enhancing the mood, but this is definitely one of those games where it could be a really cool aesthetic.
Logged

oahda
Level 10
*****



View Profile
« Reply #161 on: May 26, 2015, 10:31:02 AM »

Wow, thats awesome. Nicely done man gurrrl! There aren't a lot of 2D games where I feel like normal maps would do much for enhancing the mood, but this is definitely one of those games where it could be a really cool aesthetic.
Yeah! Ever since I've planned it I've noticed some other 2D games focusing a lot on light(ing) using it too, and it really worked for them IMO.
Logged

oahda
Level 10
*****



View Profile
« Reply #162 on: May 28, 2015, 07:10:51 AM »


UPDATE 60



Cutscene sequencer

So the last time I was working on this, before I went ahead with the normal maps in the previous update and put some work into PUSE again, I had started on a half-finished sequencer, on which I continued work today.



This is something I've never really done properly in previous games. It has usually been rather hard-coded stuff and it hasn't been taking into account whether anything has been lagging behind, making music unsynchronised in the worst case scenarios and so on. I think it should all be pretty safe this time.



Technical

The above is written as follows in the AngelScript interface:



While custom callbacks are possible to give to the sequencer, I've added — and will keep adding more — nifty shorthand functions like these seen here.

When accessed, a sequence(r) will immediately start and so everything that needs to go into it can be supplied (and it can be chained with this dot syntax as the sequencer keeps returning a reference to itself from every function) and the sequence will start next frame and automatically terminates when done. A flag is set when any sequence is running so that the submarine can read this flag and disable movement during one. Multiple sequences can run simultaneously by passing an optional index number to the sequence() function.

Segments take a duration parameter and every action after that before a new segment will go into that segment. The wait() function is a shorthand for a new segment that will do nothing but wait for the duration specified, so a new segment is automatically created after it for any new actions to go into.

Oh, and I guess it's pretty clear to anybody reading my code by now that my engine has a bit of a philosophy of global accessors for a lot of stuff that virtually every game project ever will need.



(same update on Patreon here)
« Last Edit: May 28, 2015, 12:51:21 PM by Prinsessa » Logged

oahda
Level 10
*****



View Profile
« Reply #163 on: May 28, 2015, 12:53:17 PM »

(indentation slightly messed up below after pasting)

Updated the door transition sequence with the new system as well, going from this messy and lengthy mess…

Code:
 	// If we have transitioned from the other door, now move to the new one.
  if (GUI().number("trans-door") == float(c.ID()) && !GUI().transitioning())
  {
  if (GUI().number("trans-door-state") == 1)
  {
// Start the transition.
GUI().transition(TransitionFadeIn(Colour(0, 0, 0)), 1.0);

// Get the other door.
Component @o = c.component("other");

// Move player to it and mirror submarine.
const float r = P().r().z;
P().p(o.p()).r((r <= 180) ? 180 - r : 180 + (360 - r));

// Move the camera to the player, adjust it
// immediately to any new zoom and focus,
// and mark it as moved in order to notify
// the particles floating around that they
// need to move to this new position.
camera().p(P().p()).adjust().boolean("moved", true);

GUI().number("trans-door-state", 2);
}
  else if (GUI().number("trans-door-state") == 2)
{
GUI().number("trans-door-state", 0);
GUI().number("trans-door", -1);
G().boolean("sequence", false);
  cast<Body @>(P()).positionLock(false);
}
}
  // If in contact with submarine, check for action button press.
  else if (contact && I().countJoysticks() > 0 && I().wasButtonReleased(0, B_A))
  {
  // If the monologue is visible it gets precedence.
  Component @mono = P().component("monologue");
  if (mono.visible() || mono.boolean("showing"))
  return;

  // Initiate a transition.
  if (GUI().number("trans-door") < 0)
  {
  cast<Body @>(P()).positionLock(true);
  G().boolean("sequence", true);
GUI().number("trans-door", c.ID());
GUI().number("trans-door-state", 1);
GUI().transition(TransitionFadeOut(Colour(0, 0, 0)), 1.0);
}
  }

… to this:

Code:
 	 	// Get the player as a body to lock it.
  Body @player = cast<Body @>(P());
 
  // Get the other door.
  Component @o = c.component("other");
 
  // Mirror the player's angle.
  Vectorf r = P().r();
  r.z = (r.z <= 180) ? 180 - r.z : 180 + (360 - r.z);

  // Initiate door transition sequence.
sequence().
segment().
lock(player, true, true).
segment(1.0).
transition(TransitionFadeOut(Colour(0, 0, 0))).
segment().
p      (player, o.p()).
r      (player, r).
p      (camera(), o.p()).
boolean(camera(), "moved", true).
adjust ().
wait(0.5).
segment(1.0).
transition(TransitionFadeIn(Colour(0, 0, 0))).
segment().
lock(player, false, false);

Looks just like before. Nifty system! Next up is the door opening/closing sequence.
Logged

oahda
Level 10
*****



View Profile
« Reply #164 on: May 29, 2015, 02:43:25 AM »


UPDATE 61



Letterboxing

Of course, no cutscene's any good without the obligatory ~black bars~ of Zelda games et al. that we all love. At least I do.



Sequence code:

Code:
sequence().
segment().
boolean(G(), "no-monologue", true).
segment(0.5).
letterbox(true).
wait(1.0).
segment(3.0).
activate(focus, true)
focus   (focus).
move    (surf, Vectorf(0.0f, -660.0f)).
wait(1.5).
destroy(focus).
wait(0.5).
segment(0.5).
letterbox(false).
segment().
boolean(G(), "no-monologue", false);

Gotta refactor that no-monologue thingy elsewhere, but otherwise it's pretty nice~
« Last Edit: June 01, 2015, 12:55:19 AM by Prinsessa » Logged

oldblood
Level 10
*****

...Not again.


View Profile
« Reply #165 on: May 29, 2015, 04:37:38 AM »

Love it. The cinematic bars are such a small thing but they really do add a lot to helping the player understand a cutscene is occurring (at an almost subconscious level).
Logged

Cakeprediction
Level 1
*


I'm not too sure what to put here


View Profile WWW
« Reply #166 on: May 29, 2015, 05:23:50 AM »

I can already hear that "tududududududududu" zelda secret sound :3
Logged

Huge Swords and Tentacles Devlog
"If you were to write a story with me in the lead role, it would certainly be... a tragedy"
"You have to tell your hands to freaking do the stuff until your hands know it by their tiny hand hearts"
oahda
Level 10
*****



View Profile
« Reply #167 on: May 29, 2015, 11:35:42 AM »

So easy, but adds so much!
Logged

EdFarage
Level 2
**


I can upload avatars


View Profile
« Reply #168 on: May 29, 2015, 06:49:38 PM »

that code sequence is so clean
is it see plus plus?
Logged
quan
Level 3
***



View Profile
« Reply #169 on: May 29, 2015, 07:27:19 PM »

No it's sea plus plus
Logged
oahda
Level 10
*****



View Profile
« Reply #170 on: May 30, 2015, 01:22:13 AM »

; ) )) ) ) ) )

Both wrong, actually!!

It's AngelScript. But yes, the same interface is in the C++ part of the engine and the code would be virtually identical, with the difference that I can change the Vectorf() into {} using an initialiser list in C++14. I think AS supports those too, tho, but it seems I have to define a function overload with one instead of letting the language automatically determine what to do with one like C++ can, so eh. Won't bother.
Logged

oahda
Level 10
*****



View Profile
« Reply #171 on: June 03, 2015, 01:55:18 PM »

UPDATE 62



Materials and meshes

The graphics part of the framework/engine Karhu has gotten a big update today, on top of which I'll be building more in the upcoming days, effectively revamping a lot of the system again, but adding so much more coolness and possibilities doing so.

I can now create arbitrarily shaped meshes and apply textures and colour and more to them using materials. None of these two features previously existed in the engine.

The previous system was pretty much image components and texture resources that could be brought together, with a couple of options. Text labels worked similarly, generating but the texture from a string rather than loading one from an image file.

I've wanted this sort of stuff for a while now, and after working on arbitrary meshes for an algorithm generating dynamic terrain on top of polygons for my other game Puse it was very clear in my head how I wanted to implement it to allow for similar things, and to no surprise my implementation is quite similar to that used in Unity.



The result

Everything that images support has been added to the meshes as well, including normal maps, so here is a shape filled with the same texture as in the recent update on normal maps, tinted red. All of this is controlled using a material applied to the mesh.



The C++ code for quickly creating this in main.cpp was the following:

Code:
Mesh &mesh{L().layerMain().create("mesh", "").as<Mesh>()};

Material mat;
mat.tint({180, 0, 0});
mat.texture(G().f("gfx").get<Texture>("tex"));
mat.texture(G().f("gfx").get<Texture>("bump"), "__normal");
mat.modeWrap(MODE_WRAP_REPEAT);
mesh.material(mat);

Polygonf pol;
pol.
add({100.0f, 0.0f}).
add({1400.0f, 140.0f}).
add({1000.0f, 500.0f}).
add({1600.0f, 700.0f}).
add({900.0f, 900.0f}).
add({0.0f, 400.0f});
mesh.vertices(pol);

mesh.calculateUVs();

mesh.bake();
mesh.p(-600.0f, 600.0f);
mesh.depth(-3);

Pretty darn concise!



The materials

Materials hold the information on how to colour in a mesh, be it using textures, colours, maps or all.

Unlike Unity's materials, which are resources and kind of bound to a single texture or set of textures depending on its shader, and where the wrap mode is bound to the texture as a resource, Karhu's materials are individual to each mesh (from which images and labels will be derived after I revamp the rest of the code) and while they may be cross-copied, changing the material in one mesh does not modify the material of another.

Similarly, the wrap mode is a setting in the individual material, and not bound to the texture as a resource.

Any number of textures can be added to materials with individual keys, similar to Unity. The default names expected by the default shader for colour maps (the basic texture) and normal maps are __colour (which is defaulted if no key at all is entered) and __normal, but anything goes for any custom use (custom shader code will be possible to attach to individual materials but it's not yet implemented).

Another basic setting is the colour tint, used above to tint the mesh red. Other options such as "unlit" and "autolight", currently in the image class, will be moved to materials as well, of course.



The meshes

Meshes hold some basic lists. Vertices (points making up the polygon), UV coördinates (telling the shader how to map the texture onto the polygon) and triangles are pretty much a copy of the Unity setup.

Triangles

The "triangles" are in fact not triangles, but a list of indices of the vertices that make up each triangle in the polygon. Unlike in Unity, tho, my triangles are currently hidden and automatically generated using the same triangulation algorithm that I found for Puse, tho perhaps I should open the interface to those up as well, in case somebody (me?) wants to customise it somewhere (might actually be useful for the image implementation).

UV's

UV's too can be automatically generated as seen in the code above, tho that's evidently a public function and the UV's can be set manually as well. Automatic UV's simply map onto world coördinates, making the textures simply "fill in" the mesh, tiling (if the wrap mode is set to repeat) inside of it without getting rotated or scaled or warped or anything, pretty much just making the object a mask for the texture.

Vertices

The vertices are entered using my polygon class, which is also used for defining polygonical collision masks in the physics part of the engine. The triangulation algorithm supports concave meshes just fine, tho not holes.



Conclusion

Using what I've got here, I should already pretty much be able to create the same kind of terrain generation algorithm as in Puse:



This is built up of the rocky foundation using world coördinate UV's like those my automation generates in Karhu, and using UV's mapping to each corner for the grass part on top, effectively stretching each (seamlessly tileable) image. Pretty cool stuff!

Of course this underwater game won't have much use of this kind of terrain, but similar techniques can definitely be employed to generate various rocky cliffsides and whatnot. And a simple rectangular mesh with a repeating texture will save me the pain and overhead of defining several image components with the same tiling background for the normal mapped stone wall from the previous update on normal maps.

Speaking of tiles, a system for that shall too perhaps be implemented at some point, tho.



(same update on Patreon)
« Last Edit: November 04, 2015, 04:20:46 AM by Prinsessa » Logged

oldblood
Level 10
*****

...Not again.


View Profile
« Reply #172 on: June 04, 2015, 05:04:18 AM »

Wow, love the technical breakdown. I like that you include references to Unity and compare what you're doing with it as so many indies (including myself) use Unity.

One small bit of feedback I kept forgetting to mention on your update with the puzzle and the water raising up into the room. You may want to consider having the water on the right side raised at a slightly higher level which would sink when you close one door and open the other-- allowing the water to operate similar to how a dam would raise or lower the water in different areas. If the waters levels are identical and doors are opening/closing at the same time- water wouldn't rise (I don't believe, I haven't actually researched that). Small note but I kept forgetting to mention it so throwing it out there before I forget again...
Logged

oahda
Level 10
*****



View Profile
« Reply #173 on: June 04, 2015, 05:32:07 AM »

Seems my inspiration to get a little more technical again after looking at the Obra Dinn devlog was good, then. c:

Don't worry, the water isn't actually supposed to be triggered by doing what I've shown so far, so it's going to flow from elsewhere, so I'm aware of that. The room on the other side isn't finished either. It currently lacks a ceiling, as you can see, but it's going to be properly locked in and have some other stuff added into it.
Logged

oahda
Level 10
*****



View Profile
« Reply #174 on: June 06, 2015, 01:57:26 PM »


UPDATE 63



Shameless screenshot crossposting

Already posted and wrote this in the screenshot Saturday thread (and on Twitter and will be on Patreon for that matter), but here goes:

Been refactoring engine code for days (it's getting really pretty!), so not much new done on the game but I decided I wanted to show something for this screenshot Saturday, and wow, a few colour changes and adding a parallax background without bumping did so much for the depth and atmosphere. It's finally starting to look like a real game!











(same update on Patreon)
« Last Edit: June 06, 2015, 02:21:20 PM by Prinsessa » Logged

oldblood
Level 10
*****

...Not again.


View Profile
« Reply #175 on: June 08, 2015, 06:25:20 AM »

Wow, how did I miss this update. Looks awesome! That turned out looking even better than I expected (and I had expectations). Nicely done.
Logged

oahda
Level 10
*****



View Profile
« Reply #176 on: June 10, 2015, 12:03:31 PM »

And I missed your response! Oops.

Thanks! And it's far, far from final.



UPDATE 64



Here's a really boring one for most of you, but might be interesting to those wanting to know more about my game engine Karhu and it's k00l systems. Hand Thumbs Up Right Hand Thumbs Up Right



Lots of refactoring

I won't say too much about this part, but I've spent days going through the engine code of Karhu in order to finally fix some stuff (unnecessary code, lacking code and renaming things and modifying pipelines, for example) I've wanted to fix for a while.

It includes a access to the component system, cleaner script binding, and updating some stuff to make use of neat C++14 features that I had forgotten about or were never aware of after reading up on both C++11 and C++14 (seems my project was actually still set to C++11 too despite Xcode supporting C++14).



Versatile controller scheme and input systems

This is the big one I've been coding for almost two days straight.

I've set up an input system that solves all of my problems from previous projects and ones specific to this one, while making input coding for games using Karhu a lot more elegant, with support for multiple controller schemes (optionally active at the same time) and a useful system of locking and listening to player input. It also supports multiple players with individual gamepads (or sides of a keyboard or whatever) even tho Vatnsmyrkr won't make use of that (it wasn't much extra work so I thought I might as well add it).



The schemes

The class Scheme provides two means of input: input callbacks returning input weights (a structure holding a value and a weight) and the registration of constants referring to buttons (gamepad, keyboard or mouse) which can be queried to check whether they were pressed or released during the current frame, or whether they are currently being held.

Input callbacks

A callback (a function returning a value in this case) can be registered with a name (called a key). For example, we could register a callback called "direction" and have it return the angle of the joystick as the value and how hard the stick is being pushed as the weight. We can then invoke this callback by its name/key to get this input weight.

Input constants

Similarly, constants are registered with a name/key, but instead of a callback a value is registered, which is either a gamepad button, a keyboard key or a mouse button (which can be accessed in Karhu as scoped enum constants such as CodeButton::LEFT, CodeKey::SPACE or CodeMouse::MIDDLE).

Unlike callbacks, multiple constants can be registered with the same name/key. This way we can query whether "left" is currently being held and get a positive response no matter if it is the left keyboard arrow key or the A key (for WASD controllers) that is being pressed, by having registered both by the same name/key, "left".

Shorthands

There are three shorthand functions defined in the scheme class: direction() is equivalent to input("__direction") (invoking a callback by the name/key "__direction"), and similarly we can call horizontal() and vertical() for input("__horizontal") and input("__vertical"). I chose to register these as virtually every controller system out there will want to have easy access to these values, plus it ties in with the next point, which is…

Automation

There are two more helpful functions defined: automateForKeyboard() and automateForGamepad(). These set the controller scheme up in a barebones way for keyboard or gamepad. A few options can be set before doing this, by calling the function option().

For example, gamepad automation lets you choose whether to merge D-pad and primary stick or not, whether to merge primary and secondary sticks and whether to enable diagonal output from the D-pad by combining the input of opposite axes.

If no options have been set, by default the gamepad automation will be set up with nothing merged and diagonals disabled, making direction() (or input("__direction")) return the angle and push strength of the primary joystick, input("__direction-secondary") return the same for the secondary joystick (which has no shorthand) and it will register the constants __left, __right, __up and __down for the D-pad buttons.

Vatnsmyrkr uses these default options and adds some functionality of its own by registering further callbacks and constants for its gamepad controller scheme.

Global access

Like many things in the engine Karhu, schemes are global and can be created and accessed by the global function scheme(). It takes as its argument the name/key of a scheme and returns that scheme, creating it if it does not already exist.

Thus we can create a new scheme set up with the basics for gamepad input like so:

Code:
scheme("gamepad").automateForGamepad();

This will now be accessible anywhere we want to register the scheme for use, which is the next topic.



The players

Players are special components which are set as children to player objects (in Vatnsmyrkr, a player component would be added to the submarine component) and in which multiple controller schemes can be registered. Schemes can be activated or deäctivated, so that a player may either allow for several input schemes at once (which is probably how I'll be doing it in Vatnsmyrkr; if the gamepad fails, then keyboard and mouse should be available for the rescue) or only the active one(s), depending on the game's needs.

Global access

Again, players are global. I've worked with enough games to know that the player object is something you will want to be able to access anywhere, and so the support is right there in the engine.

The global function player() takes a component (which would be the submarine in Vatnsmyrkr), registers it among the players at the position it ends up in the list (if there are no players yet, it'll be the first player, otherwise the second or third and so on) and then returns the player component. However, when invoked without a component, to access a player, the parent component (i.e. the submarine) will be returned and not the player component (because usually other objects would want to access the submarine and not the object controlling the submarine's input schemes).

Multiple schemes

We are then free to register schemes to the player component. As schemes are global, it would be as simple as this to register our newly registered scheme "gamepad" in the first player:

Code:
player().get<Player>().scheme("gamepad", scheme("gamepad"));

Let's say we also want to register a keyboard scheme. We can do that. The whole code might look something like this:

Code:
Player &p(player(submarine));
p.scheme("gamepad", scheme("gamepad").automateForGamepad());
p.scheme("keyboard", scheme("keyboard").automateForKeyboard());

Customisation

Note that the schemes are copied into the player; they're not references. Thus, after adding a scheme to a player, we can modify that scheme for that player individually, without modifying the global scheme. That way we could for example assign the same gamepad scheme to player one and two but allow them to individually customise their controllers.

Input

The player has copies of the scheme functions input(), direction(), horizontal() and vertical() as well as the functions to check constants, pressed(), held(), released().

The difference is that the player component will check all of its registered and currently active schemes, meaning that Vatnsmyrkr can check whether "action" (for the action button) was pressed and get a positive response no matter if it were the gamepad scheme (PS3 X button or Xbox 360 A button, for example) or the keyboard scheme (space key perhaps) that invoked it, if both schemes are active at the same time.

However, the most elegant way is not always to check for presses directly, but to use…

Events

Karhu's component system has already supported events for a long time, of course. However, they were a bit clunky and part of my refactoring spree dealt with this.

Initially I had thought myself clever by adding shorthands for registering events, adding a bunch of functions to the component class, such as begin(), update() and draw(). Derived component types such as physics colliders (collision masks; hitboxes), adding collision() to register a collision event callback and so on.

I did away with all of these and made it into a single function, event(), taking the name of the event as a string, meaning that begin(callback) became event("begin", callback) and so on. A few more characters to type, but so much more versatile and more importantly polymorphism-friendly, as a collider component doesn't even have to know it is a collider component in order to register a collision event, removing the need for casting.

This also ties in with my use of events for player components, which are never called on the player components themselves, but on the parent object (in Vatnsmyrkr, the submarine) and just about any other component that has chosen to listen to the player's input, which we'll be getting to later.

With the player component comes three new events: "press", "hold" and "release", corresponding to the accessors for the constants. Whenever a button registered by a constant is pressed, held or released, an event will be sent to the player component's parent as well as any registered listeners. This way, the Submarine doesn't need to constantly check whether the light toggle is pressed by itself, but only respond to the light toggle constant's pressed (or released, as I tend to do it in my games) event and run the appropriate code to toggle the light.

But it extends further than this, and this is where it gets really neat…

Listeners

So whereas the player component's parent owns its input and gets automatically notified by any input event, other components can tell the player that they want to be listening in on certain constants, and so they too will get notified when the button(s) linked to that constant is/are pressed, held or released (for which they too need to implement callbacks for the events "press", "hold" and "release", showing again why the event system update was a good thing).

For example, my door script listens to the "action" button in order to take the player through a door when that button is pressed. But it is not the only eavesdropper. The monologue (the little speech bubble that pops up above the submarine when the person inside has something to say, which expands to a textbox when the action button is pressed) too is keen on knowing when this button is pressed.

Here we might run into a problem (and my old code before I designed this system did run into that problem): what happens when we are in front of a door and there is a monologue speech bubble as well? Will pressing the action button make the submarine go through the door or open up the speech bubble? Or both? Even tho I'd tried to write a little code in the old system (which wasn't a system at all; just loose, direct input queries), they were in fact both invoked before. That's a bug!

This is where the next little gem comes in…

Priorities

When a listener is registered, it can choose to set a priority number (higher number means higher priority). If no number is set, this will default to 0. When multiple components are listening to the same constant and a press, hold or release event is invoked for it, the player component will go through them in order of priority, starting with those of the highest priority. Only the listener with the highest priority, and any listeners sharing the same priority, will be told about the event, and the rest will be ignored and these listeners won't know that any button was pressed or released or is currently being held down.

This way, as long as there is no listener with a higher priority, all listeners will still be told because they have the same default priority of 0. My doors have a priority of 0. However, I've given the priority of 100 to my monologue after making the design decision that monologues should always go first if there are multiple things trying to get invoked by the action button in the same place. Now the system will make sure that only the monologue will get told about the button press and the door will be ignored.

Now, this would be a problem too, if all listeners were constantly listening. If the monologue was always listening, you could never go through a door, even if there was no monologue in the way, because the monologue would still be listening and get prioritised and the door would be ignored. This is why listeners can, and must, somewhere in their code, make sure to stop listening (which does not unregister them as listeners, but simply puts them on hold) when they do not need to check for any button (for example, when you're not in front of the door) and start listening only when they do (when you're in front of the door).

Locking and autolocking

Inputs (both callbacks and constants) can be locked either individually or all together (locking all input for all schemes of the player), which is useful in situations where the player and its listeners must really not be allowed to press a specific button or the like.

When a constant is locked, its pressed, held and released states will always be false and no events will be invoked. When a callback is locked, it will return value and weight both of 0.

There is also the feature of autolocking. Enabling this will automatically lock everything when the game is in a sequence or paused, which is what one would want for most players.
« Last Edit: June 10, 2015, 12:33:03 PM by Prinsessa » Logged

oahda
Level 10
*****



View Profile
« Reply #177 on: June 12, 2015, 11:24:17 AM »


UPDATE 65



Time for more boring code! Durr...?



AngelScript finally set up as intended

I hadn't understood how to do everything properly when first implementing AngelScript, but I decided that it was about time I made something about my original, poor scripting support.

Short summary

Every component (object in the game; components can contain other components) can have one or more script files attached to it. These may contain functions of a certain signature that are bound as callbacks to events in the component (i.e. pieces of code that are set to be run at specific happenings in the game or the component, such as when the component is first added onto the scene, or when it is removed, or during every frame and so on).

The problem

I'd been reading the files in such a way that every function was isolated from everything else in the program, including the other functions in the scripts. I couldn't declare non-event functions to reüse code in the event functions or anything. I couldn't create variables in the script file to share between events. Nothing.

Fixed!

With really few changes, I solved this all, and made sure that while every script will now see the other functions in the file as well as variables and whatever is possible to create in AngelScript, they wouldn't see outside of that file, into script files of other components, or even other script files of the same component. This way, simple names can be used without the risk of clashing with the name of a function or variable in another script.



Bilingual callbacks

This opened up a possibility I really needed, but had been lacking so far: sending any functions from AngelScript as callbacks into anything in the engine, not just components. I'll use an actual example to show you what I mean.

You probably didn't read my last update, but in short, I made a system where components could "listen" after key/button/mouse presses/clicks (or releases or button being held), but in order for this to work properly, these listeners would have to contain some manual code to "unlisten" when they don't need to listen, and to listen again only when they need to.

The only way I could allow for this to be done through scripting before was to have a function (a rather bulky one at that) to turn this on and off in the system that manages the listeners. Not only did the code get lengthy and cluttery, but it had to do some stuff which gave a bit of unnecessary overhead to the program (making it slower altho it probably would never be noticed).

Either way, I did want to solve this with callbacks, allowing the components to, when they register themselves as listeners in the system that handles those, to give the system a function containing the instructions to tell it when they should not be listening and when they should. The system could then call this function to see what to do when it's already going through all the listeners, removing the overhead and making it all rather pretty.

The problem

The functions could be given from AngelScript this way, but also from C++. AS functions and C++ functions are not the same type of data. I can't just pass either one as a callback and expect it to work.

Therefore I had to set up another type of data that would receive either of these and then program it in such a way that it would handle things differently depending on whether it contains an AngelScript function or a C++ function, but with the exact same input, so that the system updating the listeners can simply tell my "wrapper" data type to run the function it contains with the arguments/parameters/values that are given, all without the listener system knowing whether it is calling an AS function or a C++ function. That's up to the wrapper data type to deal with for it.

Long story short

I wrote a bunch of code all day, exploring new parts of C++ I had never had the need to look at before, and which weren't even available in the language until relatively recently (variadic templates, anyone?), so that was really exciting.

Eventually I got it all working well and so callback functions can now be passed through C++ or AS, get put into one of these wrappers, and then the wrapper has a function called run(), that, well, runs the function it contains in the way appropriate (again, C++ and AS functions don't work the same way) and possibly gives a value back in return.

This way the listener system can check whether a listener is listening by doing something like this, where condition is a wrapper of a callback function:

Code:
if (listener.condition.run(*listener.component)) { /* do stuff here... */ }

Where listener is a stucture containing the callback condition as well as the actual component that's to be notified about any button presses or the like. The function run will in this case return a boolean, i.e. "true" or "false", telling the system whether to listen (true) or not (false).



Ava's question to the reader!

So, I know my last post was overly lengthy. Tried to keep it shorter this time. Am I keeping it simple enough so that somebody without programming knowledge can kind of follow along (while still namedropping a few interesting things like "variadic templates" to those who do know C++ or whatever)? .-.
Logged

b∀ kkusa
Global Moderator
Level 10
******



View Profile
« Reply #178 on: June 12, 2015, 02:34:29 PM »

Most of the time i have no clue what you're talking about
but it's still fascinating to read whenever this is updated.

the submarine is really cute when in motion.


Logged
oldblood
Level 10
*****

...Not again.


View Profile
« Reply #179 on: June 12, 2015, 03:47:44 PM »

You could very well have a future as a professor of game design somewhere...
Logged

Pages: 1 ... 7 8 [9] 10 11 ... 18
Print
Jump to:  

Theme orange-lt created by panic