CTMLoader does not correctly assign the instanceof the geometry - three.js

if I modify the sample webgl_loader_ctm.html and in the routine callbackModel() add these lines of code:
if (geometry instanceof THREE.Object3D) alert("THREE.Object3D");
else if (geometry instanceof THREE.Geometry) alert("THREE.Geometry");
else alert("Unknown instanceof geometry");
I was expecting the second alert to be activated but instead I got the third. As my code depends on the instanceof to be correct, does anybody know what do I have to change in the loader in order to fix this?

There are two main loading methods built into CTMLoader: Depending on the useBuffers parameter, it creates BufferGeometry or Geometry. You might want to first check against BufferGeometry (which is not a subclass of Geometry) too, or set the useBuffers parameter to false (I'm not familiar with CTM, so I don't know if the file formats are different for buffered vs normal).
Anyway, Three.js handling of classes can be a bit hard to keep track of, and some related classes may not share a parent class. You may or may not agree, but I would maybe do the instanceof checking "if it looks like a duck, it is a duck" -style. So checking for some property that only exists in Object3D or similar objects:
if (geometry.lookAt) {
alert("Looks like Object3D, I know what to do with this");
} else if (geometry.vertices) {
alert("Looks like Geometry with some vertices, I know what to do with this");
} else {
alert("I dont know how to handle this object");
}

Related

How can I remove a group from a Three.js scene on click?

I have a group of objects (actually 3D text on an arc) that I want to remove from the scene on a specific click. Does .remove not work on groups? Here's basically what I have:
$(".inscript").on("mousedown", function(event){
var x = scene.getObjectByName("inscriptArc");
scene.remove(x);
});
This answer seems to suggest you can (remove a group from a scene using .remove), but it isn't working for me.
THREE.Scene.getObjectByName returns the first instance of a child with that name. If you have multiples you won't catch them by calling it once.
To remove all instances I would use the THREE.Object.traverse(fn(child){}) function ie:
var children_to_remove = [];
scene.traverse(function(child){
if(child.name == "inscriptArc"){
children_to_remove.push(child);
}
});
children_to_remove.forEach(function(child){
scene.remove(child);
});
you might be able to just do
scene.traverse(function(child){
if(child.name == "inscriptArc"){
scene.remove(child);
}
});
but I think there are some cases where this could cause an error if you are loading/removing anything from the scene asyncronously - because removing a child might throw an error when that child's children get traversed. Not sure, so I would try the simple one and swap with the more complicated one if it causes issues.
Yes, you definitely can remove a group of objects. You might have named children of the object instead of the actual group. Function getObjectByName did not always work as promised in older three.js releases so maybe try this.
scene.children.forEach(child => child.name == "inscriptArc" ? scene.remove(child) : null)

Unity 2D: NullReferenceException. How to add two manager scripts?

I am quite new to Unity, so please bear with my horrible explanation. I have followed a tutorial on youtube about a 2D fighting game: https://www.youtube.com/watch?v=n8S3WgVoOmo&t=3319s
I will provide my code below.
In the linked video, the video-maker makes one PlayerManager script which controls both my player and duplicates of my player. Through a simple AI, the duplicate becomes my enemy and we fight. This much works as expected.
Now I wanted to change it a bit like a 2D platformer. I took the PlayerManager and split it into two. AIManager (same code as in tutorial overall) and a PlayerManager with some changes. This also worked well and I am able to move and the AI also recognises me. The problem comes from a script called DamageScript. It recognizes if and when I take damage, and triggers the relevant animation.
When the DamageScript was connected to just the PlayerManager, when I hit the AI or AI hit me, whoever got hit showed the Damage animation. But after the split I did, with the separate Managers for AI and Player, I have two options. Either I hit the AI, he does Damage animation and when he hits me I get and error. Or he hits me, I have the Damage animations and when I hit him I get an error. The error is this:
NullReferenceException: Object reference not set to an instance of an object
DamageScript.OnTriggerEnter2D (UnityEngine.Collider2D col) (at Assets/Scripts/DamageScript.cs:19)
This is the original DamageScript:
public class DamageScript : MonoBehaviour {
void OnTriggerEnter2D(Collider2D col)
{
if(col.transform.root != transform.root && col.tag != "Ground" && !col.isTrigger)
{
if (!col.transform.GetComponent<AIManager>().damage && !col.transform.GetComponent<PlayerManager>().blocking)
{
col.transform.GetComponent<AIManager>().damage = true;
col.transform.root.GetComponentInChildren<Animator>().SetTrigger("Damage");
}
}
}
}
This way my Player does the Damage animation, I get the error when I hit. So, I figured something like this would work, but I guess I don't really know how to code it:
void OnTriggerEnter2D(Collider2D col)
{
if(col.transform.root != transform.root && col.tag != "Ground" && !col.isTrigger)
{
line 11 if (!col.transform.GetComponent<AIManager>().damage && !col.transform.GetComponent<AIManager>().blocking)
{
col.transform.GetComponent<AIManager>().damage = true;
col.transform.root.GetComponentInChildren<Animator>().SetTrigger("Damage");
}
else
{
if (!col.transform.GetComponent<PlayerManager>().damage)
{
line 19 col.transform.GetComponent<PlayerManager>().damage = true;
col.transform.root.GetComponentInChildren<Animator>().SetTrigger("Damage");
}
}
}
}
}
Not surprisingly, it doesn't work and I still get crashes on some hits.
Any help would be appreciated, if possible.
Thank you!
From the situation you've described, I'll assume the error is being, primarily, by faulty refactoring of responsibility delegation.
More specifically, I believe it's sufficiently clear that the tutorial you've followed didn't follow single responsibility principle properly, and instead, implemented multiple responsibilities (player and AI) on a single class/file, to save time for the video, or to simplify the tutorial.
Later, when splitting the responsibilities to two scripts and two objects, you, as a beginner, was/is unaware of some details and/or pitfalls involved in reference management, and so are failing to assign references to both objects, or failing to handle missing references as the scripts are now split.
The problem arises because, if your player and AI now have different sets of scripts; one with each manager, but neither with both (unlike before, when both player and AI objects had "both"); then, either on the first or second if*manager.damage statement, the manager in question would not be found with GetComponent because it's not in that object, and upon trying to access fields/properties/methods on a null reference, the NullReferenceException would be thrown.
The solution is simply to do the proper null checks before accessing such fields/properties/methods, to guard from the exception and proceed to the second if statement if the first's manager is found to be null.
While at it, might as well cache the queries to make things better, as RetiredNinja recommended in the comments.
Code:
void OnTriggerEnter2D(Collider2D col) {
if(col.transform.root != transform.root && col.tag != "Ground" && !col.isTrigger) {
//Cache to avoid multiple queries and to simplify access
var playermanager = col.transform.GetComponent<PlayerManager>(); //One of these won't be found and will receive null instead
var aiManager = col.transform.GetComponent<AIManager>(); //One of these won't be found and will receive null instead
var animator = col.transform.root.GetComponentInChildren<Animator>();
if (aiManager != null //Null-check was missing
&& !aiManager.damage && !aiManager.blocking) { //Much nicer
aiManager.damage = true;
if(animator!=null)
animator.SetTrigger("Damage");
}
else {
if (playerManager != null && !playerManager.damage) {
playerManager.damage = true;
animator.SetTrigger("Damage");
}
}
}
}

Problems using a std::map containing a class without copy operator (Gdiplus::Image)

It seems I am trying to fill a std::map with objects that are not copyable, and I have not achieved to do it yet.
General problem
I want to use std::map in order to store some objects of a type called Image (More precisely, it is Gdiplus::Image). I cannot write things like:
map<string, Gdiplus::Image> loadedImages ;
Gdiplus::Image newImage( CString("totoro.png") );
loadedImages.insert(std::pair<string, Gdiplus::Image>( "totoro", newImage ) );
Function "insert" seems to be the problem here. The compiler says:
'Gdiplus::Image::Image' : cannot access private member declared in class 'Gdiplus::Image'
I am not sure that it is the right explaination, but it seems that "Image" lacks of a public method used in function "insert". (Copy operator ? Copy constructor ?).
What I have tried
I tried to use references in the map, but it seems putting references in containers never works. I tried to use raw pointers, but I had got errors when I tried to delete all the images in the destructor. I happened across this other (and quite similar) question and I have begun to care about smart pointers. So now, I am trying, as recommended in the answer, with std::shared_ptr. However, my case is slightly different.
I want to write a function "find" that returns an image. "find" gives the image found in the map if the key (its path) exists, else it loads the image, add it to the map and returns it. So I cannot create a new image inside the parenthesis as I need the pointer.
The only version I came up with, that can compile is:
(Drawer.h)
#include <map>
#include <memory>
#include <Gdiplus.h>
using std::map ;
using std::shared_ptr ;
class CDrawer
{
public:
CDrawer(void);
~CDrawer(void);
void drawImage(string p_pathToPicture)
private:
map<string, shared_ptr<Gdiplus::Image>> m_loadedImages ; // Keep every image in memory instead of loading them each time. Each image has its path as a key.
Gdiplus::Image* findImage(string& p_path); // get the image from the map if the image is already loaded, else load it.
};
(Drawer.cpp) (Constructors and destructors are empty)
void CDrawer::drawImage(string p_pathToImage)
{
// get the bounding rectangle of the image
//...
Gdiplus::Image* l_image = findImage(p_pathToImage);
// Draw the image.
//...
}
Gdiplus::Image* CDrawer::findImage(string& p_pathToImage)
{
auto iterator = m_loadedImages.find(p_pathToImage);
if (iterator == m_loadedImages.end() ) // image not found, so we have not already loaded it
{
shared_ptr<Gdiplus::Image> l_newImage( new Gdiplus::Image( CString( p_pathToImage.c_str()) ) ); // Load the image (I know I have to add error code)
m_loadedImages.insert( std::pair<string, shared_ptr<Gdiplus::Image>>( p_pathToImage, l_newImage ) ); // Add the image to the list
return l_newImage.get() ;
}
else return iterator->second.get() ; // image found, so it is already loaded and we provide the existing one.
}
But it gives the following error during run time, when the destructor of Drawer is called:
Unhandled exception at 0x00C18CEE in MyProgramm.exe: 0xC0000005: Access violation reading location 0x02F36D78
Does someone knows where I am wrong, or if there is a simpler or better solution?

AS2, Referencing a Changing Object Name

so I was wondering if there was a way to reference different objects on stage with he same method to save repeating lots of lines of code. This is what I have right now
function bossKilled(i:Number):Void {
trace("Boss Killed!");
kills ++;
_root.bossDeath.gotoAndPlay(2);
_root["pirate"+i+"Active"] = false; //name of variable would be pirate1Active
_root["pirate"+(i+1)+"Active"] = true; //name of variable would be pirate2Active
bossDeath._x = _root["pirate"+i+"Active"]._x;
bossDeath._y = _root["pirate"+i+"Active"]._y; }
However, this reference does not actually affect the variables. I was wondering if this was possible, and if so, what am I doing wrong?
Thanks.
Not sure what you try to achieve ... pirate1Active is a BOOL. A BOOL has no _x or _y property (nor any other).
If you are not sure where to find your objects in the object tree, you can use the debugger or add some traces on the MCs timeline, like trace (_parent);
Consider switching to AS3, it is much more object oriented and has better tools support.

Not uniform argument order in backbone event triggers. How can I work around it?

someModel.bind("all", function(eventName, model, XXXXX, YYYYY) {
options.myStuff = "MyStuff et all";
self.trigger(eventName, model, ??????, ?????);
}
XXXXX, YYYYY, ????? mark the problem: Where are the options?
I want to augment the options and retrigger any event on a new context. The problem is that backbone puts options in 4th position in some events ("change:xxx", "error", etc..) and in 3rd position in some others("change", "reset") making it impossible for me to know where in the arguments the options are, and to know where in the trigger i should put the enhanced options. Is there any other way except the obvious separate snippet for every individual event?
UPDATE: Looking at the backbone source, an acceptable solution for me would be to patch Backbone to always include a third argument in every event, so that options are always in the same 4th position. It is a simple change but I am kind of hoping for a more user space solution.
As far as I can tell, the options are always the last argument passed to the callback. You could use the arguments object of the callback to modify the options. https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments
For example
var x=new Backbone.Model();
x.bind("all", function(e) {
console.log("x : "+e);
console.log(arguments[arguments.length-1]);
});
var m=new Backbone.Model( {name:"a"} );
m.bind("all",function(e){
console.log("m : "+e);
var args=Array.prototype.slice.call(arguments);
args[args.length-1].modified=true;
x.trigger.apply(x,args);
});
m.set({name:"b"});

Resources