[SOLVED] Error implementing picker_raycast in PlayCanvas engine

I am using the playcanvas-stable.min.js engine without the editor and I’m having trouble trying to implement picker_raycast. I have attached the script to a camera but there seems to be a problem. I get the alert(“start”) but I don’t get the alert(“result”). I also don’t seem to be getting any errors in the console even without the minified version of the engine. Any help would be appreciated.

var PickerRaycast = pc.createScript('pickerRaycast');

// initialize code called once per entity
PickerRaycast.prototype.initialize = function() {
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onSelect, this);alert("init");
};

PickerRaycast.prototype.onSelect = function (e) {
    var from = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.nearClip);
    var to = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.farClip);
    alert("start"); //This executes

    //There seems to be problem in the line below
    var result = this.app.systems.rigidbody.raycastFirst(from, to);

//I tried to replace the above line with this but it still didnt work
/*
    this.app.systems.rigidbody.raycastFirst(from, to, function (result) {
        alert("raycast is working");
    });
*/
    alert("result"); //This doesn't execute
    if (result) {
        var pickedEntity = result.entity;
        pickedEntity.translate(3,0,0); //This doesn't execute
    }
};
1 Like

Hi @Gamer_Wael,

Are physics working in your project? The rigidbody system requires Ammo.js to be available and physics enabled and running.

If you are using the engine directly, you need to make sure that you have Ammo.js loaded in your project before loading the playcanvas library.

Take a look at this example:

2 Likes

Thanks a lot. That was the problem. I haven’t seen many tutorials for the engine alone. Do you know where I can learn about it to avoid making such mistakes again? And also is the “751ec5f” the version of the script ammo.751ec5f.js?

1 Like

There isn’t much documentation in working directly with the engine, the best place to learn is the examples that come with the engine:

Regarding Ammo.js yes that’s the current legacy version, you are good using it. Though the physics system was recently updated to use WASM (web assembly) and support that way the latest version of Ammo.js (which comes with performances improvements as well).

To learn how to add that version to your project, for the moment the only way I see would be to export a build of a physics enabled project from the editor and study how the loader app does that.

1 Like

Thanks and another thing that I wanted to ask was that I created html/css ui in the online editor like:

I have the html, css and ui.js files but I don’t know how to add to the scene in the engine only version. I have tried it like so but it doesn’t work:

app.assets.loadFromUrl('ui.js', 'script', function (err, asset) {
    app.root.addComponent('script');
    app.root.script.create("ui", {
        attributes: {}
    });
});

That’s great, you are almost there, what you need in addition is to load using loadFromUrl your html and css files and pass them in the attributes: {} object.

This will mimic in your code only project the action that you do in the editor by drag and dropping those files in the script properties.

Both the script and the html/css files are assets, which means that you need to load them all using that method since you aren’t using the editor to do that for you.

1 Like

Hmm… I can’t get it to work. Can you tell me what I’m doing wrong here…
In my ui.js I have:

var Ui = pc.createScript('ui');

Ui.attributes.add('css', {type: 'asset', assetType:'css', title: 'CSS Asset'});
Ui.attributes.add('html', {type: 'asset', assetType:'html', title: 'HTML Asset'});

And in my index.html:

            app.assets.loadFromUrl('html', 'script', function (err, asset) {
                app.root.addComponent('script');
                app.root.script.create("html", {
                    attributes: {}
                });
            });

            app.assets.loadFromUrl('css', 'script', function (err, asset) {
                app.root.addComponent('script');
                app.root.script.create("css", {
                    attributes: {}
                });
            });

            app.assets.loadFromUrl('ui.js', 'script', function (err, asset) {
                app.root.addComponent('script');
                app.root.script.create("ui", {
                    attributes: {
                        css: css,
                        html: html
                     }
                });
            });

If you are using engine only, you can just put the HTML and CSS into the page itself as essentially, the UI script is loading the text in the CSS and HTML file and adding the DOM to the page.

3 Likes

You are better off doing what @yaustar suggests if you don’t have any specific reason to use a Playcanvas script to load your html/css (e.g. to use it in the editor at a later point).

If you continue using a script to load your html/css the code should look like this (not tested):

app.assets.loadFromUrl("pathToHtmlFile.html", "html", function(err, htmlAsset) {
  app.assets.loadFromUrl("pathToCssFile.css", "css", function(err, cssAsset) {
    app.assets.loadFromUrl("pathToUi.js", "script", function(err, uiAsset) {

      app.root.addComponent("script");
      app.root.script.create("ui", {
        attributes: {
          css: cssAsset,
          html: htmlAsset
        }
      });
    });
  });
});
2 Likes

To remove that ES5 callback hell, you could promisify the PlayCanvas API:

class AppSimplified {
	// Resolve the promise with cached asset, if it is already loaded.
	// Works around the limitation of `loadFromUrl` API which only callbacks once
	cached: {
		[url: string]: pc.Asset
	} = {};
	async fetch_pc_html(url) {
		return new Promise((resolve, reject) => {
			if (url in this.cached) {
				resolve(this.cached[url])
				return;
			}
			this.app.assets.loadFromUrl(url, "html", (err, asset) => {
				if (err) {
					reject(err);
				} else {
					this.cached[url] = asset;
					resolve(asset);
				}
			})
		})
	}

	async fetch_pc_css(url) {
		return new Promise((resolve, reject) => {
			if (url in this.cached) {
				resolve(this.cached[url])
				return;
			}
			this.app.assets.loadFromUrl(url, "css", (err, asset) => {
				if (err) {
					reject(err);
				} else {
					this.cached[url] = asset;
					resolve(asset);
				}
			})
		})
	}
}

And then you could write something like:

		var appSimplified = new AppSimplified();
		await import("ui.js");
		var asset_html = await appSimplified.fetch_pc_html("ui.html");
		var asset_css = await appSimplified.fetch_pc_css("ui.css");
		var ent = new pc.Entity("ui");
		ent.addComponent("script");
		ent.script.create("ui", {
			attributes: {
				html: asset_html,
				css: asset_css
			}
		});
		this.app.root.addChild(ent);

But your ui.js script still needs to append the HTML/CSS into the DOM (Seemore demo ui.js has an example for that)

1 Like

Thanks a lot for the help guys. I decided to go with what @yaustar suggested. @Leonidas I tried your script too and it works just as well but I found the other method easier.
P.S.: Is there a way to mark a topic as closed or solved?

2 Likes

You can append [SOLVED] to the title of your initial post or one of the moderators will do.

1 Like