Reference Error when converting Unity code to PlayCanvas

var HeadBobController = pc.createScript('headBobController');

HeadBobController.attributes.add('_enable', { type: 'boolean', default: true });

HeadBobController.attributes.add('_amplitudeStart', { type: 'number', default: 0.015, min: 0, max: 0.1 });

HeadBobController.attributes.add('_frequency', { type: 'number', default: 10, min: 0, max: 30 });

HeadBobController.attributes.add('_camera', { type: 'entity' });

HeadBobController.attributes.add('_cameraHolder', { type: 'entity' });

// initialize code called once per entity

HeadBobController.prototype.initialize = function () {

    this._toggleSpeed = 3;

    this._startPos = new pc.Vec3(0, 0, 0);

    _startPos = _camera.locaPosition;

};

// update code called every frame

HeadBobController.prototype.update = function (dt) {

    if (!_enable) return;

    CheckMotion();

    ResetPosition();

    _camera.LookAt(FocusTarget());

};

HeadBobController.prototype.CheckMotion = function () {

    this.speed = new pc.Vec3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;

    if (speed < _toggleSpeed) return;

    if (!_controller.isGrounded) return;

    PlayMotion(FootStepMotion());

};

Vec3.prototype.FootStepMotion = function () {

    Vec3.Zero = new Vec3(0, 0, 0);

    pos.y += Mathf.Sin(Time.time * _frequency) * _amplitude;

    pos.x += Mathf.Cos(Time.time * _frequency / 2) * _amplitude * 2;

    return pos;

};

HeadBobController.prototype.ResetPosition = function () {

    if (_camera.locaPosition == _startPos) return;

    _camera.locaPosition = Vec3.Lerp(_camera.locaPosition, _startPos, 1 * Time.dt);

};

Vec3.prototype.FocusTarget = function () {

    Vec3.Zero = new Vec3(transfrom.position.x, transform.position.y + _cameraHolder.locaPosition.y, transform.position.z);

    pos += _cameraHolder.forward * 15;

    return pos;

};

I’m getting 2 reference errors for this script, the first one is for line 51" Vec3.prototype.FootStepMotion = function () {" and the second one is for the line 21 “_startPos = _camera.locaPosition;”

The goal of this script is to create a head bobbing effect in FPS game prototype i’m making in PC. I wrote this script by watching a Unity tutorial about the same subject. I couldn’t find much resources or examples in Play Canvas or Java Script so i had to refer to the Unity example.

I’d appreciate anyone who would help about the subject

There are a number of issues with this so let’s run through some of them:

var HeadBobController = pc.createScript('headBobController');

//.....
Vec3.prototype.FootStepMotion = function () {

    Vec3.Zero = new Vec3(0, 0, 0);

    pos.y += Mathf.Sin(Time.time * _frequency) * _amplitude;

    pos.x += Mathf.Cos(Time.time * _frequency / 2) * _amplitude * 2;

    return pos;

};

HeadBobController.prototype.ResetPosition = function () {

    if (_camera.locaPosition == _startPos) return;

    _camera.locaPosition = Vec3.Lerp(_camera.locaPosition, _startPos, 1 * Time.dt);

};

Vec3.prototype.FocusTarget = function () {

    Vec3.Zero = new Vec3(transfrom.position.x, transform.position.y + _cameraHolder.locaPosition.y, transform.position.z);

    pos += _cameraHolder.forward * 15;

    return pos;

};

For some reason, you are using Vec3 prototype instead of HeadBobController. Vec3 doesn’t exist in this scope and I assume you were trying to declare some functions for the HeadBobController so let’s fix that first:

var HeadBobController = pc.createScript('headBobController');
HeadBobController.attributes.add('_enable', { type: 'boolean', default: true });
HeadBobController.attributes.add('_amplitudeStart', { type: 'number', default: 0.015, min: 0, max: 0.1 });
HeadBobController.attributes.add('_frequency', { type: 'number', default: 10, min: 0, max: 30 });
HeadBobController.attributes.add('_camera', { type: 'entity' });
HeadBobController.attributes.add('_cameraHolder', { type: 'entity' });

// initialize code called once per entity

HeadBobController.prototype.initialize = function () {
    this._toggleSpeed = 3;
    this._startPos = new pc.Vec3(0, 0, 0);
    _startPos = _camera.locaPosition;
};

// update code called every frame

HeadBobController.prototype.update = function (dt) {
    if (!_enable) return;
    CheckMotion();
    ResetPosition();
    _camera.LookAt(FocusTarget());
};

HeadBobController.prototype.CheckMotion = function () {
    this.speed = new pc.Vec3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;
    if (speed < _toggleSpeed) return;
    if (!_controller.isGrounded) return;
    PlayMotion(FootStepMotion());
};

HeadBobController.prototype.FootStepMotion = function () {
    Vec3.Zero = new Vec3(0, 0, 0);
    pos.y += Mathf.Sin(Time.time * _frequency) * _amplitude;
    pos.x += Mathf.Cos(Time.time * _frequency / 2) * _amplitude * 2;
    return pos;
};

HeadBobController.prototype.ResetPosition = function () {
    if (_camera.locaPosition == _startPos) return;
    _camera.locaPosition = Vec3.Lerp(_camera.locaPosition, _startPos, 1 * Time.dt);
};

HeadBobController.prototype.FocusTarget = function () {
    Vec3.Zero = new Vec3(transfrom.position.x, transform.position.y + _cameraHolder.locaPosition.y, transform.position.z);
    pos += _cameraHolder.forward * 15;
    return pos;
};

In FootStepMotion, it looks like you were trying to create a new pc.Vec3 and use it to calculate a new position based on time and cos/sin waves. However, Vec3 doesn’t exist as a variable so it’s going to give you an undefined error.

Also, you are trying to create a new Vec3 object from PlayCanvas which is in the pc object. So again, that class doesn’t exist, you have to use pc.Vec3. I suspect you want something like this:

HeadBobController.prototype.FootStepMotion = function () {
    var pos = new pc.Vec3(0, 0, 0);
    pos.y += Mathf.Sin(Time.time * _frequency) * _amplitude;
    pos.x += Mathf.Cos(Time.time * _frequency / 2) * _amplitude * 2;
    return pos;
};

Time also doesn’t exist. You will need to track this yourself by creating a new class property and updating it in the update call.

var HeadBobController = pc.createScript('headBobController');
HeadBobController.attributes.add('_enable', { type: 'boolean', default: true });
HeadBobController.attributes.add('_amplitudeStart', { type: 'number', default: 0.015, min: 0, max: 0.1 });
HeadBobController.attributes.add('_frequency', { type: 'number', default: 10, min: 0, max: 30 });
HeadBobController.attributes.add('_camera', { type: 'entity' });
HeadBobController.attributes.add('_cameraHolder', { type: 'entity' });

// initialize code called once per entity

HeadBobController.prototype.initialize = function () {
    this._time = 0;
    this._toggleSpeed = 3;
    this._startPos = new pc.Vec3(0, 0, 0);
    _startPos = _camera.locaPosition;
};

// update code called every frame

HeadBobController.prototype.update = function (dt) {
    this._time += dt;

    if (!_enable) return;
    CheckMotion();
    ResetPosition();
    _camera.LookAt(FocusTarget());
};

HeadBobController.prototype.CheckMotion = function () {
    this.speed = new pc.Vec3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;
    if (speed < _toggleSpeed) return;
    if (!_controller.isGrounded) return;
    PlayMotion(FootStepMotion());
};

HeadBobController.prototype.FootStepMotion = function () {
    var pos = new pc.Vec3(0, 0, 0);
    pos.y += Mathf.Sin(this._time * _frequency) * _amplitude;
    pos.x += Mathf.Cos(this._time * _frequency / 2) * _amplitude * 2;
    return pos;
};

HeadBobController.prototype.ResetPosition = function () {
    if (_camera.locaPosition == _startPos) return;
    _camera.locaPosition = Vec3.Lerp(_camera.locaPosition, _startPos, 1 * Time.dt);
};

HeadBobController.prototype.FocusTarget = function () {
    var pos = new Vec3(transfrom.position.x, transform.position.y + _cameraHolder.locaPosition.y, transform.position.z);
    pos += _cameraHolder.forward * 15;
    return pos;
};

Mathf doesn’t exist but I’m assuming you want to use the JS standard library math functions

HeadBobController.prototype.FootStepMotion = function () {
    var pos = new pc.Vec3(0, 0, 0);
    pos.y += Math.Sin(this._time * _frequency) * _amplitude;
    pos.x += Math.Cos(this._time * _frequency / 2) * _amplitude * 2;
    return pos;
};

You are also trying to call the functions you’ve created but you need to always reference the this object when doing so as the functions belong to the ScriptType object. Right now, you are calling those functions as though they are in the global scope but they don’t exist and will give you an undefined error.

Same with accessing the scripttype properties.

Here’s a quick fix up:

var HeadBobController = pc.createScript('headBobController');
HeadBobController.attributes.add('_enable', { type: 'boolean', default: true });
HeadBobController.attributes.add('_amplitudeStart', { type: 'number', default: 0.015, min: 0, max: 0.1 });
HeadBobController.attributes.add('_frequency', { type: 'number', default: 10, min: 0, max: 30 });
HeadBobController.attributes.add('_camera', { type: 'entity' });
HeadBobController.attributes.add('_cameraHolder', { type: 'entity' });

// initialize code called once per entity

HeadBobController.prototype.initialize = function () {
    this._time = 0;
    this._toggleSpeed = 3;
    this._startPos = new pc.Vec3(0, 0, 0);
    this._startPos = this._camera.locaPosition;
};

// update code called every frame

HeadBobController.prototype.update = function (dt) {
    this._time += dt;

    if (!this._enable) return;
    this.CheckMotion();
    this.ResetPosition();
    this._camera.LookAt(this.FocusTarget());
};

HeadBobController.prototype.CheckMotion = function () {
    this.speed = new pc.Vec3(this._controller.velocity.x, 0, this._controller.velocity.z).magnitude;
    if (this.speed < this._toggleSpeed) return;
    if (!this._controller.isGrounded) return;
    this.PlayMotion(this.FootStepMotion());
};

HeadBobController.prototype.FootStepMotion = function () {
    var pos = new pc.Vec3(0, 0, 0);
    pos.y += Math.Sin(this._time * this._frequency) * this._amplitude;
    pos.x += Math.Cos(this._time * this._frequency / 2) * this._amplitude * 2;
    return pos;
};

HeadBobController.prototype.ResetPosition = function () {
    if (this._camera.locaPosition == this._startPos) return;
    this._camera.locaPosition = Vec3.Lerp(this._camera.locaPosition, this._startPos, 1 * Time.dt);
};

HeadBobController.prototype.FocusTarget = function () {
    var pos = new Vec3(transfrom.position.x, transform.position.y + _cameraHolder.locaPosition.y, transform.position.z);
    pos += this._cameraHolder.forward * 15;
    return pos;
};

There’s lots of issues like this throughout the code and stem from using Unity keywords/classes etc instead of PlayCanvas which I will leave for you to have a go at.

3 Likes

Wow it was a fast and a great reply!! You guys are really the best!!! I got another error after i fixed the issues you pointed out. I get this error now: “Cannot read properties of undefined (reading ‘velocity’)”
for this line: “this.speed = new pc.Vec3(this._controller.velocity.x, 0, this._controller.velocity.z).magnitude;”
and i believe the reason for this is that i haven’t declared a _controller variable in the script because _controller is a variable for the character controller component of Unity which doesn’t exist in the Play Canvas. I couldn’t find anything to replace it with in the PC. I posted that in the discord channel and someone posted a script named characterController but i don’t think it’s what i’m looking for

I have a feeling that going through the Crash Course on PlayCanvas (Crash Course - Make a Game | Learn PlayCanvas) would give you a better grounding to work from/start from

There’s a lot of ground to cover in this script alone that would take a long time to get through and starting from our tutorials and documentation would be better to build from.

2 Likes

@Ronin Please read this posted on Playcanvas Blog.

PlayCanvas versus Unity WebGL | PlayCanvas.

One of the bigger differences is that Unity uses C# were as Playcanvas uses Javascript. All programming languages share a lot of similar functionalities however there are differences in how you logically have to think about things.

There is not a one to one port of code. However, Unity uses Vectors and shaders and a lot of the same terminology that is the same but programmatically must be handled differently.

I suggest the same as @yaustar.

Once you start getting the basics under your belt I think you will find that you are seeing some similar methods/terms as Unity. Welcome to Playcanvas.

1 Like

Ok i will do that. I already know some of the basics shown in the video which is not enough obviously. But pls don’t tell me the time i spent on this code is wasted. Is there a way i can make this code work or do i have to write a script from the top with what i’ll learn. If it’s the latter it’s gonna take a lot of time for me to get there. What do you suggest?

The main issue is that the script is using Unity API instead of PlayCanvas so someone would have to go through and ensure that is correctly converted. This means going through each issue and correcting it one by one.

While I can quickly do that for you, it’s not going to help you in the long run when you write your next script etc and unfortunately, I can’t spend that time going step by step with you.

Going to back this, yes you are correct that you haven’t defined or assigned a value to this._controller so you have to go back to see what object that should be from the Unity tutorial and how to get the equivalent in your new PlayCanvas project and also how to get the velocity (or equivalent) from that object.

Other issues include line 44 where Vec3.Lerp doesn’t exist in PlayCanvas in the same syntax but we do have the functionality that you have to adapt.

On the same line, there is use of Time.dt which again, doesn’t exist in PlayCanvas so how do you get dt or the equivalent to that function?

I found two threads on PC forums about this Head Bobbing effect subject and in both of them PC moderators direct the user to a Unity tutorial link and they say the logic is the same in JS with C#. Here:

I’m not trying to blame anybody here but don’t you think it’s kind of misleading and confusing to say someone to "This is similar to Unity, just look at this Unity tutorial and try it on JS"then saying “JS&Play Canvas don’t operate like Unity&C#, you need to know both languages well to work this out.” Again i’m not blaming you or anybody here, but please don’t direct people to Unity tutorials on these forums again. Just direct them to a Java Script&Play Canvas source so they don’t waste time trying to convert a C# script to JS like me. Or just say we don’t know how to do it. I hope you see my point here.

“While I can quickly do that for you, it’s not going to help you in the long run when you write your next script etc and unfortunately, I can’t spend that time going step by step with you.” I’m well aware of that and respect that. I never expected for you to do it in the first place.

Thanks again for the support and the time you spent for me, i appreciate it sincerely

The logic is the same because ultimately it’s the same math and Unity share similar architectural of entity/gameObject components and scripts but the API is different and so is the language so people have to adapt the logic to work in PlayCanvas.

In both of those threads, that is pretty much what the people replying are saying, ‘Here is a Unity tutorial on Head “Bobbing” which you can study and transfer to PlayCanvas:’

Even if we point people to an example in three.js for instance, the API is still going to be different and it still has to be adopted.

As far as I can tell, the intention of the logic in your script is fine, the syntax and API use needs to change to work in JS and PlayCanvas

I tell you what though, if you can share a public project fork of this, I would be more up for creating an Office Hours video for this as that could be helpful for others and be quicker for me to do I think :thinking:

@Ronin This was a very valid suggestion from @yaustar. Please take a look at this members post.

“The logic is the same because ultimately it’s the same math and Unity share similar architectural of entity/gameObject components and scripts but the API is different and so is the language so people have to adapt the logic to work in PlayCanvas.”
Yes and you need to have a certain level of knowledge in both programming languages and APIs to make it work and that’s the main cause of my failure. The problem i see here is learning the basics of Java Script still won’t make me able to create a script like this on my own without any guidance or reference. I saw even expert Unity devs check YouTube for tutorials when they’re stuck.You can’t figure everything yourself sometimes even if you’re an expert

Sorry if i reflected my frustration on you. Struggling and failing on something may make people look very negative. If it’s not gonna be any trouble for you i’d gladly accept your help. Here’s the link to my project:
https://playcanvas.com/project/986170/overview/fps-prototype-fork

Office Hours #19 - Converting ‘Head Bob’ Unity tutorial to PlayCanvas

Fixed project: https://playcanvas.com/project/986420/overview/unity-head-bob

3 Likes

Really great video, i can’t thank you enough for that!! I have one question. This if statement: “if (!this._controller.isGrounded) return;” is this used to apply head bobbing motion to jumping? Is this possible to shake the camera when the player character lands after a jump with this script?

If we look at the update function and uncomment the line in question

HeadBobController.prototype.CheckMotion = function () {
    var velocity = this.entity.rigidbody.linearVelocity.clone();
    velocity.y = 0;
    var speed = velocity.length();

    if (speed < this._toggleSpeed) return;
    
    if (!this._controller.isGrounded) return;
    
    this.PlayMotion(this.FootStepMotion());
};

We can see that if isGrounded is false, the function returns and doesn’t call PlayMotion.

This means that if the player is not deemed to be on the ground by the controller script, it will not head bob.

Not without modification and understanding of the script. Things to take into account:

  • How do you know if the player has just landed or is moving across it normally?
  • How do you ensure that the side to side bobbing doesn’t interfere with camera shake? Should they be layered on top of each other or be independent?
1 Like

“How do you know if the player has just landed or is moving across it normally?” In the unity tutorial project we try to convert to JS, inside the player movement script, the coder declared a bool variable which checks whether the player is on the ground or not. They do this to prevent jumping infinitely. I fix that issue with a different method in my movement script but i understand that if i want to implement head bobbing effect to the jumping mechanic the best way to go is to declare a isGrounded bool. But you say how do you know if the player has just landed, yeah that’s the tricky part, i thought about that before. I think it is doable with an “onContact” function. When the player collides with the ground object’s collider, camera shake is triggered. I need to create a jump state and the player has to be in the jump state when it collides with the ground object.

“How do you ensure that the side to side bobbing doesn’t interfere with camera shake? Should they be layered on top of each other or be independent?” I believe the best way to go here is to declare a new function like “JumpMotion” and handle the head bobbing motion on jump command inside that function. That function will be triggered on jump command with an if statement like "if(isGround = false)
Then i’ll have to call the “JumpMotion” function within the CheckMotion function i believe. Of course there’s much more to it than that but i think this is the main logic.

This is a very tricky thing to do. No wonder in none of the tutorials they don’t do the camera motion on jump. Anyway thanks again for the reply man, i’m just brainstorming now