Jump to content

  • Log In with Google      Sign In   
  • Create Account

Juliean

Member Since 29 Jun 2010
Online Last Active Today, 07:09 AM

Topics I've Started

Visual scripting: Breakpoint debugging

26 December 2014 - 01:53 PM

Hello,

 

I've got a very specific issue with my visual-scripting system. I've got command-nodes, which are called in order of connection. Then I've got a special type of node called "Trigger", which is called by the application as an entry-point for code execution. You can look at the attached screenshot to see how it works. There is an "EventTickTrigger", which is called in the application like that:

event::Instance& instance = *eventComponent.pInstance;
instance.Trigger<EventTickTrigger>(dt);

So the way I used to have it here is that the call to Trigger() would simply put the first connected node on the trigger into the instances node execution queue, and once per frame when "Run(dt)" was called on the instance, all triggered nodes would execute.

 

I've now run into problems with this, where one node type, e.g. "SetTalkState", could fire a certain trigger ("BeginTalkTrigger"), and this triggers code logically should be run before the nodes after "SetTalkState" can execute. For example, the Player-Script has a "StopMovement"-command that is executed when BeginTalkTrigger is called on it. A certain event might now begin a talk-scene, and direct the player to move a certain distance using a "MoveEntity"-command directly after. If the BeginTalkTrigger only executes after the next frame tick in the Run()-call, "MoveEntity" will already be called, and "StopMovement" will cancel the scripted move route of the player. I hope everyone gets the idea - script code just has to execute directly when a trigger is called in C++-code to behave correctly.

 

So the problem now arises with debuggin. I've already implemented a simple debugging system where one can set breakpoints to certain nodes that would halt script and game execution and let the user inspect the state of the script. With the old system, there would be no problem at all, since the only point breakpoints would be called, is inside this one loop where Run() is called on each script-instance, so everything happens in one place, where I can easily add mechanism for handling halting and resuming of the application-logic in case a breakpoint was hit.

 

Now that every Trigger<>()-call should directly execute its appended code, things might get ugly. Triggers are supposed to be called from arbitrary points in the users/plugins code. That means that i.e. in case of a code that checks for sensor interaction and calls an enter/leave-trigger, that code would have to be written in a way that both inner and outer loops state are stored, as well as any code that is to be called after, has to be skipped and be resumed after the user continues the script execution:

static std::map<std::pair<ecs::EntityHandle, ecs::EntityHandle>, bool> mCollided;

if(!vEntities.empty() && !vActivatorEntities.empty())
{
	for(auto& entity : vActivatorEntities) // this has to be stored
	{
		auto pPosition = entity->GetComponent<Position>();

		const auto vPosition = pPosition->v;

		for(auto& sensorEntity : vEntities) // also that
		{
			if(sensorEntity == entity)
				continue;

			auto pSensorPosition = sensorEntity->GetComponent<Position>();
			auto pSensor = sensorEntity->GetComponent<Sensor>();

			const auto pair = std::make_pair(entity, sensorEntity);
			const bool collided = mCollided.find(pair) != mCollided.end();

			const auto vDistance = vPosition - pSensorPosition->v;
			if(vDistance.length() <= pSensor->radius)
			{
				if(!collided)
				{
					auto& entityObject = ecs::createObject(entity);

					// we would have to somehow catch the breakpoint here
					triggerEvent<EventEnterSensorTrigger>(sensorEntity, &entityObject);
					mCollided.emplace(pair, true);
				}
			}
			else
			{
				if(collided)
				{
					// we would have to somehow catch the breakpoint here
					triggerEvent<EventLeaveSensorTrigger>(sensorEntity, &ecs::createObject(entity));
					mCollided.erase(pair);
				}
			}
		}
	}
}

It would be very awkward having to do this for every single time you call a trigger, not to mention that there is a lot more state that would have to be stored that isn't even seen here, so doing this is really not an option.

 

So my question is, does anybody have some other ideas for handling script breakpoints, in the case of script code that is executed directly from c++-code? How is this handled in other scripting languages? I know that there isn't probably anything like those "triggers" in that languages, but you still have to call code from the c++-side of things. Any ideas/suggestions?


Templated constructor "overwrites" regular copy-ctor

16 December 2014 - 04:20 PM

EDIT: Ok, I'm a dumbass. Defining the second non-const reference copy-ctor is the right way, and it only didn't support the symbol because I just switched to the test-project which didn't have a compile-dependency set for the engine-lib. Why don't you forget I asked that... I'll just leave it up though in case someone as confused as me might have a similar issue, sometimes.

 

Hello,

 

I've got a problem in a class of my engine. This is the relevant part of the class for the issue:

class __declspec(dllexport) Variable
{
public:
    template<typename Type>
    explicit Variable(Type& value);
    template<typename Type>
    explicit Variable(const Type& value);

    Variable(const Variable& variable);
}

So until now I only used to have the second templated ctor, in which as in the following code:

 

 

Variable var; Variable var2(var);

Variable var;
Variable var2(var);

The copy-ctor was being called. But now that I've also included the non-const reference templated ctor, this one gets called instead. After thinking it through this appears logical, since Type& is the closer match to being passed a "Variable&" then "const Variable&", even though this is the more specific version. So if I'm not mistaken, this at least is how c++ is supposed to behave.

 

But how can I solve this? Things I've already tried and didn't work:

 

- declaring a second "Variable(Variable& variable)" copy-ctor. MSVC will complain with a warning C4521 (second copy ctor declared) and not generate any symbol for that version, so I can't link anymore.

 

- removing the "const" from the ctor altogther. Not really a viable option from begin on, but just in case someone mentiones it. This class cannot be used that way.

 

Does somebody know any easy solution for this? There has got to be one, right? I really don't want having to do stuff like

Variable var;
Variable var2((const Variable&)var);

or

Variable copyVariable(const Variable& source)
{
    return Variable(source);
}

for this. Any suggestions, or is there no way to make him call the right copy-ctor by modifiying the declarations somehow?


Transparent backbuffer

09 October 2014 - 08:56 AM

Hello,

 

I'm trying to get a transparent backbuffer, for integrating my DX-rendered gui better with the rest of the OS. I've got a few questions/issues with that though:

 

- I've found out that I'm supposed to use DwmExtendFrameIntoClientArea for that. It also kind of works, though I get some sort of strange alpha/z-ordering issues, as you can see in Issue.png. Here is the code I'm using the create the window (plain old windows w/o any libary): (see edit below)

 

- Even though I can now see whats underneath the empty areas of my GUI... I still can't click/do anything else there, since the empty backbuffer is still overlapping the whole screen. Is there any way of "deactivating" certain areas of my backbuffer/invisible window, so that my application behaves like it was rendered using normal windows? Note that I can't just move the backbuffer as the main window you see moves, because there might be popup-windows of all sorts, which can go over the area of the main window... so any method for willingly deactivate area of a window?

 

EDIT: Ok, I was too fast in blaming the windows-part for this. Apparently the issue is with my rendering, because its those areas with the weird issues, where I render an alpha-blended texture over a solid texture. That happens for all my texts, and also for the icon in the upper left. Now I can get rid of this effect for most parts, by using alpha testing via clip (when alpha in the texture is 0), but what am I supposed to do when I have semi-transparent values, like the edges of the icon in the upper left? I can't just disable alpha-writing all together, since after clearing everything in the backbuffer is 0 (transparent). But I need to somehow only write alpha when the value is greater then what is currently in the buffer.

 

I'm rendering my gui with painters-algorithm and batched, so everything that is on top gets drawn last, in one go. Is there any way to achieve this kind of alpha-effect to fix my issue? So what I want summed up is:

 

- Every pixel with alpha > 0 should get drawn.

- In case texture pixel alpha is greater than backbuffer pixel alpha, the alpha value is written

- Otherwise, the pixel should still be blended to the content thats already in the backbuffer, but alpha value should stay the same.

 

EDIT2: Ugh, I'm just too tired to think. Of course I can configurate the AlphaSrcBlend and AlphaDestBlend-states to fig this issue. Havn't used those in a while, huh. So the rendering problems are solved now, though Issue#2 is still up. Any ideas for that?


std::unique_ptr issues

08 September 2014 - 08:06 AM

Hello,

 

I'm just in the process of replacing objects that owned and deleted a pure pointer with std::unique_ptr. However, I'm running into some issues:

 

- I've been heavily using forward declaration to speed up compile times, like here:

class Trigger;

class Instance
{
    typedef std::unordered_map<std::wstring, Trigger*> TriggerMap;
public:

    ~Instance(void)
    {
        for(auto& trigger : m_mTrigger)
        {
            delete trigger.second;
        }
    }
    
private:

    TriggerMap m_mTrigger;
}

However, I keep getting "warning C2027" in visual studio, in that an incomplete type can't be deleted when replacing the pure pointer with std::unique_ptr. Some of the times leaving the empty dtor fixes it, however in most cases, especially containers, I have to replace the forward-declaration with a direct include in the header. Is there some way around this? It really bugs me having to do this on so many occasions, could make recompile times significantly larger (already kind of a problem since I'm heavily using templates)...

class Trigger;

class Instance
{
	typedef std::unordered_map<std::wstring, std::unique_ptr<Trigger>> TriggerMap;
public:

	~Instance(void); // defined in cpp-file
	
private:

	TriggerMap m_mTrigger;
}

- I also keep getting compilation-errors with this kind of container-of-pointer setup when the class has a default copy ctor/operator. So the above example wouldn't even compile unless I put:

class Trigger;

class Instance
{
	typedef std::unordered_map<std::wstring, std::unique_ptr<Trigger>> TriggerMap;
public:

        Instance(void) = default;
        Instance(const Instance&) = delete;
	~Instance(void); // defined in cpp-file

        void operator=(const Instance&) = delete;
	
private:

	TriggerMap m_mTrigger;
}

Now I don't completely mind it, in fact I have been very sloppy about doing this before, since a class the won't compile that way shouldn't have been copyable before. Still, I wonder whether there is any shorthand to this? E.g. adding a std::unique_ptr member will automatically disable copy ctor/operator for the class, I wonder if thats possibly within a container class too?


decltype(vector<Type>) doesn't compile

31 August 2014 - 04:37 AM

Hello,

 

I have a chain of classes with a pair of templated set/get methods that require different return semantics based on the type.

float pod = GetAttribute<float>(0);
SomeClass* pObject = GetAttribute<SomeClass>(1);
std::vector<float>& vPOD = GetAttributeArray<float>(2);
const std::vector<SomeClass*>& vObjects= GetAttributeArray<SomeClass>(2);

auto & decltype seemed to do the job:

template<typename Type>
auto Variable::GetValue(void) const -> decltype(returnSemantics<Type>::value)
{
	ACL_ASSERT(m_type == getVariableType<Type>::type);
	ACL_ASSERT(m_isArray == isArray<Type>::value);

	return getObjectData<Type, isPrimitiveType<Type>::value>::GetData(m_pData, m_objectType);
}

returnSemantics is a templated struct that gets specialized for the destired return type:

template<typename Type, bool isPOD = isPrimitiveType<Type>::value>
struct returnSemantics
{
	static Type value = Type();
};

template<typename Type>
struct returnSemantics<Type, true>
{
	static Type value;
};

template<typename Type>
Type returnSemantics<Type, true>::value = Type();

template<typename Type>
struct returnSemantics<Type, false>
{
	static Type* value;
};

template<typename Type>
Type* returnSemantics<Type, false>::value = nullptr;

However, I can't seem to get this to work with the "array"-overload of the method:

template<typename Type>
auto AttributeComponent::GetAttributeArray(unsigned int slot) -> decltype(core::returnSemantics<std::vector<Type>>::value)
{
	return GetAttribute<std::vector<Type>>(slot);
}

When I use this code like in the examples above, I get:

error C2893: Funktionsvorlage 'unknown-type acl::event::AttributeComponent::GetAttributeArray(unsigned int)' konnte nicht spezialisiert werden
1>          Mit den folgenden Vorlagenargumenten:
1>          'Type=int'

which translates to something along the lines of "function template '...' could not be specialized with template arguments 'Type=int'. Why? Is there anything wrong with the decltype-usage?

decltype(core::returnSemantics<std::vector<Type>>::value)

I'm using the "Type" argument to form a vector, and use the vector as template-argument for my returnSemantics-structure... I'm using Visual Studio 2013, does anybode see the issue here?

 

EDIT: Also fails without the specific overload, even using

std::vector<float>& vTest = GetAttribute<std::vector<float>>(0);

which calls the first posted method fails with the same compilation error...


PARTNERS