[SOLVED] Drag and Drop UI, and Then Clone it

So, basically, if you press a button it will place the entity. I also need it to drag and drop so you can clone it any where. But, the error is that when I clone the entity, the drag and drop script will crash or something, saying “cannot read screen of null”. HELP!

Editor:
https://playcanvas.com/editor/scene/884474

Code for Drag and Drop:

var CharDrag = pc.createScript('charDrag');

CharDrag.attributes.add('handle', {type: 'entity', default: null, title: 'Handle'});
CharDrag.attributes.add('axis', {type: 'string', default: "y", title: 'Axis', description: 'lock drag to axis: x, y or xy'});

CharDrag.prototype.postInitialize = function() {
    
    if( ! this.handle ) this.handle = this.entity.parent.findByName("CharDragHandle");
    
    if( this.handle ) this.addHandleListeners();
    else throw new Error( "CharDrag has no handle" );
    
    this.isDragging = false;
    this.touchId = -1;

    this.mousePos = new pc.Vec3();
    
    this.anchorPos = this.handle.getLocalPosition().clone();
    
    this.screen = this.getUIScreenComponent();
};

CharDrag.prototype.getUIScreenComponent = function() {
    return this.handle.element.screen.screen;           **THIS IS WHERE THE ERROR IS**
};

CharDrag.prototype.addHandleListeners = function() {
    
    this.handle.element.useInput = true;
    
    this.handle.element.on(pc.EVENT_MOUSEDOWN, this.onPressDown, this);
    this.app.mouse.on(pc.EVENT_MOUSEUP, this.onPressUp, this);
    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onPressMove, this);
        
    if( this.app.touch )
    {
        console.log("initing touches");
        this.handle.element.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchEnd, this);
        this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchEnd, this);
        this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
    }    
    
    this.on('destroy', function() {
        
        this.handle.element.off(pc.EVENT_MOUSEDOWN, this.onPressDown, this);
        this.app.mouse.off(pc.EVENT_MOUSEUP, this.onPressUp, this);
        this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onPressMove, this);

        if( this.app.touch )
        {
            this.handle.element.off(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
            this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchEnd, this);
            this.app.touch.off(pc.EVENT_TOUCHCANCEL, this.onTouchEnd, this);
            this.app.touch.off(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
        }
    });
    
};

CharDrag.prototype.onTouchStart = function(ev) {
    var touch = ev.changedTouches[0];
    this.touchId = touch.identifier;
    this.startDrag( ev.x, ev.y );
    ev.event.stopPropagation();
};
CharDrag.prototype.onTouchMove = function(ev) {
    for(var i=0; i< ev.changedTouches.length; i++ ) 
    {
        var t = ev.changedTouches[i];
        if( t.id == this.touchId )
        {
            ev.event.stopPropagation();
            this.updateMove( t.x, t.y );
            return;
        }
    }
};
CharDrag.prototype.onTouchEnd = function(ev) {
    for(var i=0; i< ev.changedTouches.length; i++ ) 
    {
        var t = ev.changedTouches[i];
        if( t.id == this.touchId )
        {
            ev.event.stopImmediatePropagation();
            this.touchId = -1;
            this.endDrag( t.x, t.y );
            return;
        }
    }
};
CharDrag.prototype.onPressDown = function(ev) {
    ev.event.stopImmediatePropagation();
    this.startDrag(ev.x,ev.y);
};
CharDrag.prototype.onPressUp = function(ev) {
    ev.event.stopImmediatePropagation();
    this.endDrag(ev.x,ev.y);
};
CharDrag.prototype.onPressMove = function(ev) {
    this.updateMove(ev.x,ev.y);
    ev.event.stopImmediatePropagation();
};
        
CharDrag.prototype.startDrag = function(x,y) {
    this.isDragging = true;
    this.setMouseXY(x,y);
};
CharDrag.prototype.updateMove = function(x,y) {
    if( this.isDragging ) this.setMouseXY(x,y);
};
CharDrag.prototype.endDrag = function(x,y) {
    this.isDragging = false;
    this.setMouseXY(x,y);
};
CharDrag.prototype.setMouseXY = function(x,y) {
    this.mousePos.x = x;
    this.mousePos.y = y;
};

CharDrag.prototype.update = function(dt) {
    this.updateDrag();
};

CharDrag.prototype.updateDrag = function() {
    
    if( ! this.isDragging ) return ;    
    
    var device = this.app.graphicsDevice;
    var xOffs = this.handle.element.anchor.x*device.width;
    var yOffs = this.handle.element.anchor.y*device.height;
    var scale = 1/this.screen.scale;
    
    var screenX = (this.axis == 'x' || this.axis == 'xy') ? (this.mousePos.x-xOffs)*scale : this.anchorPos.x ;
    var screenY = (this.axis == 'y' || this.axis == 'xy') ? (-this.mousePos.y+yOffs)*scale  : this.anchorPos.y ;
    
    this.handle.setLocalPosition(screenX,screenY,0);
};

Code for Cloning:

var Paste = pc.createScript('paste');

// initialize code called once per entity
Paste.prototype.initialize = function() {
    this.app.keyboard.on(pc.EVENT_KEYUP, this.button, this);
};

// update code called every frame
Paste.prototype.update = function(dt) {
    
};
    
Paste.prototype.button = function(event) {
    console.log(event.key);
    if (event.key === pc.KEY_ENTER) {
        var centity = this.entity.clone();
        this.app.root.findByName("DevLevel").findByName("Screen").addChild(centity);
        centity.setPosition(this.entity.getPosition());
        centity.enabled = true;
        centity.name = "ClonedKnight";
    }
};

Hi @BloodStorm0606,

It seems that your drag/drop script is trying to request a reference to the Screen entity/element before it’s in place.

Try moving this line from your initialize method:

this.screen = this.getUIScreenComponent(); 

To your updateDrag method and paste will work:

CharDrag.prototype.updateDrag = function() {
    
    if( ! this.isDragging ) return ;    
    
    if(!this.screen) this.screen = this.getUIScreenComponent();
    
    var device = this.app.graphicsDevice;
    var xOffs = this.handle.element.anchor.x*device.width;
    var yOffs = this.handle.element.anchor.y*device.height;
    var scale = 1/this.screen.scale;
    
    var screenX = (this.axis == 'x' || this.axis == 'xy') ? (this.mousePos.x-xOffs)*scale : this.anchorPos.x ;
    var screenY = (this.axis == 'y' || this.axis == 'xy') ? (-this.mousePos.y+yOffs)*scale  : this.anchorPos.y ;
    
    this.handle.setLocalPosition(screenX,screenY,0);
};

Thanks! It works fine now!

1 Like