afro360 afro360 - 3 months ago 9
jQuery Question

jQuery animate follow element and remove on collosion

Im trying to make a game using Phonegap. I have created a draggable element called

.player
. I also have elements called
.bad-guys
.

The .bad-guys animate using the basic

$(".bad-guys").animate({
left: '250px',
opacity: '0.5',
});


I need help to make the
.bad-guys
animate and follow the draggable
.player
and slowly get closer then
.remove()
itself once it has made contact with the
.player
.

https://jsfiddle.net/9vcyhy2r/

UPDATE



So I've been experimenting with the following but have some bugs Im hoping to get some help with:

$(document).ready(function() {

$( ".player" ).draggable();

var mouseX = 0, mouseY = 0;
$( ".player" ).bind( "mousemove", function(e) {
mouseX = e.pageX;
mouseY = e.pageY;
});

$(document).on('mousemove', '.player', function () {
var follower = $(".bad-guy");
var xp = 0, yp = 0;
var loop = setInterval(function(){
xp += (mouseX - xp) / 50;
yp += (mouseY - yp) / 50;
follower.css({left:xp, top:yp});
}, 80);
});

makeBadguy();
function makeBadguy() {
var numRand = Math.floor(Math.random() * 501);
var divsize = 800;
var posx = (Math.random() * ($('body').width() - divsize)).toFixed();
var posy = (Math.random() * ($('body').height() - divsize)).toFixed();
$newdiv = $("<div class='bad-guy'></div>").css({
'left': -posx + 'px',
'top': -posy + 'px'
});
$newdiv.appendTo('body').delay(2000).fadeIn(100, function () {
makeBadguy();
});
}

}); // jQuery End


The problem, as you can see in this fiddle is ALL the
.bad-guy
is being referenced and causing the jumping action. How can I refer to each individual
.bad-guy
and position them accordingly?

Here's another Fiddle showing one
.bag-guy
following the
.player
. How can I get all the
.bad-guys
to follow?

Answer

This was quite fun to play with.

I implemented the chase mechanic with setIntervals instead of animate and made the bad guys chase the players. Thought this would be easier and smoother than trying to do it with mousemove and animate.

Since it listens for keydown to place boxes, make sure the snippet frame is focused before starting. Place Players with G and place Bad Guys with B.

I made it with classes because I was looking for an excuse to use them for a while now and this was as good as any.

I recommend playing it full page when playing from snippet.

Here is the JSFiddle for those that prefer it.

{
    let _latestid = 1;
    const timertext = $("#counter");
    window.Game = class Game {
        constructor() {
            this._gameover = true;
            this._interval;
            this._timerStart;
            this._timerEnd;
            this._timerInterval;
            this._gameOverCallbacks = [];

            this.players = [];
            this.badguys = [];

            this._startCollisionCheck();
        }

        _startCollisionCheck() {
            clearInterval(this._interval);
            this._interval = setInterval(() => {
                if (this._gameover)
                    return clearInterval(this._interval);
                if (this.players.length == 0 || this.badguys.length == 0) return;
                this.players.some(player => {
                    if (this._gameover) return true;
                    const [playerx, playery] = player.position;
                    const [pdimx, pdimy] = player.dimensions;
                    const [px1, px2, py1, py2] = [playerx, (playerx + pdimx), playery, (playery + pdimy)];
                    this.badguys.some(badguy => {
                        if (this._gameover) return true;
                        const [badguyx, badguyy] = badguy.position;
                        const [bdimx, bdimy] = badguy.dimensions;
                        const [bx1, bx2, by1, by2] = [badguyx, (badguyx + bdimx), badguyy, (badguyy + bdimy)];

                        if (px1 < bx2 && px2 > bx1 && py1 < by2 && py2 > by1) {
                            this.gameOver();
                            return true;
                        }
                    });
                });
            }, 1);
        }

        _startTimer() {
            this._timerStart = Date.now();
            this._timerEnd = undefined;
            clearInterval(this._timerInterval);
            this._timerInterval = setInterval(() => {
                timertext.text(this.timePretty);
            }, 10);
        }
        _stopTimer() {
            this._timerEnd = Date.now();
            clearInterval(this._timerInterval);
        }

        get time() {
            return (this._timerEnd ? this._timerEnd : Date.now()) - this._timerStart;
        }
        get timePretty() {
            const _t = this.time;
            const _m = parseInt(_t % 1000 / 10).toString();
            return parseInt(_t / 1000) + "." + "00".substring(0, 2 - _m.length) + _m;
        }
        set ongameover(fn) {
            this._gameOverCallbacks.push(fn);
        }

        _fireGameOverCallback() {
            this._gameOverCallbacks.forEach(fn => {
                fn();
            });
        }
        addPlayer(x, y) {
            const player = new Player(_latestid++, x, y)
            this.players.push(player);
            this.gameStart();
            return player;
        }

        addBadguy(x, y, player) {
            const badguy = new Badguy(_latestid++, x, y, player);
            this.badguys.push(badguy);
            this.gameStart();
            return badguy;
        }

        gameOver() {
            this._stopTimer();
            this.players.forEach(player => player.element.remove());
            this.players = [];
            this.badguys.forEach(badguy => badguy.element.remove());
            this.badguys = [];
            this._gameover = true;
            this._fireGameOverCallback();
            return this;
        }

        gameStart() {
            if (this._gameover && this.players.length > 0 && this.badguys.length > 0) {
                this._gameover = false;
                this._startTimer();
                this._startCollisionCheck();
            }
            return this;
        }
    }

    class Player {
        constructor(id, initX = 0, initY = 0) {
            this.id = id;
            this.element = $("<div>").addClass("player");
            this.element.css({
                left: initX,
                top: initY
            }).appendTo("body");
            this._draggable();
        };

        get position() {
            const _p = this.element.position();
            return [_p.left, _p.top];
        }
        get dimensions() {
            return [this.element.width(), this.element.height()];
        }

        _draggable() {
            this.element.draggable({
                containment: "#container",
                scroll: false
            });
        }
    }

    class Badguy {
        constructor(id, initX = 0, initY = 0, player) {
            this.id = id;
            this.element = $("<div>").addClass("bad-guy");
            this.element.css({
                left: initX,
                top: initY
            }).appendTo("body");
            this.player = player;
            this._player;
            this._isChasing = false;
            this._interval;
            this._minSpeed = 0.001;
            this._maxSpeed = 0.06;
            this._speed = [0.002, 0.002];
            this.intervalStep = 10;
        }

        get __speed() {
            return this._speed;
        }
        set __speed(speed) {
            let [_x, _y] = speed;
            _x = _x > this._minSpeed && _x < this._maxSpeed ? _x : (_x < this._minSpeed ? this._minSpeed : this._maxSpeed);
            _y = _y > this._minSpeed && _y < this._maxSpeed ? _y : (_y < this._minSpeed ? this._minSpeed : this._maxSpeed);
            this._speed = [_x, _y];
        }

        get player() {
            return this._player
        }
        set player(player) {
            if (player == undefined || player == null) return;
            if (!(player instanceof Player))
                return console.warn("Player was not set because the send object was not a player");
            this._player = player
        }
        get isChasing() {
            return this._isChasing;
        }
        get position() {
            const _p = this.element.position();
            return [_p.left, _p.top];
        }
        get dimensions() {
            return [this.element.width(), this.element.height()];
        }

        _position(x, y) {
            this.element.css({
                left: x,
                top: y
            });
        }
        _randomizeSpeed() {
            let [speedx, speedy] = this.__speed;
            const sign = Math.random() >= 0.5 ? 1 : -1;
            speedx += sign * speedx * Math.random() * 0.1;
            speedy += sign * speedy * Math.random() * 0.1;
            this.__speed = [speedx, speedy];
        }
        startChase() {
            if (!this.player) {
                console.warn("Badguy could not start chasing because it was not set after a player.");
                return this;
            }
            this.stopChase();
            this._interval = setInterval(() => {
                const pl = this.player;
                if (!pl) this.stopChase();

                const [playerx, playery] = pl.position;
                const [thisx, thisy] = this.position;
                const [difx, dify] = [(thisx - playerx), (thisy - playery)];
                const [winx, winy] = windowDim();
                this._randomizeSpeed();
                const [speedx, speedy] = this.__speed;
                this._position((thisx + (-1 * Math.sign(difx) * winx * speedx)), (thisy + (-1 * Math.sign(dify) * winy * speedy)));
            }, this.intervalStep);
            return this;
        }

        stopChase() {
            clearInterval(this._interval);
            return this;
        }
    }

    const windowDim = () => {
        const _w = $(window);
        return [_w.width(), _w.height()];
    }
}

const game = new Game();
game.ongameover = () => { 
    //attach a handler to gameover event to alert the player on gameover
    alert("Game Over! \n Time: " + game.timePretty + " seconds.");
}
$(document).on("keydown", function(e) {
    // "G" creates players
    if (e.which == 71)
        game.addPlayer();
    // "B" creates bad guys that will focus a random player
    // I recommend playing with one player
    else if (e.which == 66)
        game.addBadguy((Math.random() * 400), (Math.random() * 400), game.players[Math.round(Math.random() * (game.players.length - 1))]).startChase();
});

$(() => {
    $("#container").focus()
});
* {
    box-sizing: border-box;
}

#container {
    margin: 0;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}

.player {
    width: 50px;
    height: 50px;
    background-color: green;
    position: absolute;
}

.bad-guy {
    width: 50px;
    height: 50px;
    background-color: black;
    position: absolute;
    //border-radius: 50%;
}

#counter {
    position: fixed;
    top: 10px;
    right: 10px;
    font-size: 24px;
    font-family: "segoe ui";
    color: orange;
    background: rgba(255, 255, 255, .5);
    padding: 10px;
    border-radius: 20%;
    border: 1px solid lightgreen;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<div id="container" tabindex="-1"></div>
<span id="counter">0.000</span>