Movement.qml 7.23 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*[[
<Property name="obstacle1" formalName="Obstacle 1" type="ObjectRef" />
<Property name="obstacle2" formalName="Obstacle 2" type="ObjectRef" />
<Property name="hasPhysics" formalName="Has Physics?" animatable="False" type="Boolean" default="False" />
<Property name="hasAI" formalName="Has AI?" animatable="False" type="Boolean" default="False" />
<Property name="speed" formalName="Speed" animatable="False" type="Float" default="300" />

<Handler name="onMouseDown" formalName="On Mouse Down" />
<Handler name="onMouseUp" formalName="On Mouse Up" />
<Handler name="moveUp" formalName="Move Up" />
<Handler name="moveDown" formalName="Move Down" />
<Handler name="moveLeft" formalName="Move Left" />
<Handler name="moveRight" formalName="Move Right" />
<Handler name="stopHorizontal" formalName="Stop Horizontal" />
<Handler name="stopVertical" formalName="Stop Vertical" />

<Event name="onPlayerScore" />
<Event name="onComputerScore" />
19
<Event name="ballCollision" />
20 21 22 23 24 25 26 27 28 29 30 31 32
]]*/

import QtStudio3D.Behavior 1.0

Behavior {
    //Properties exposed to the studio
    property var obstacle1
    property var obstacle2
    property var hasPhysics
    property var hasAI
    property var speed

    //Properties used as global variables
33 34 35
    property bool mouseDown: false
    property vector3d input: Qt.vector3d(0, 0, 0);
    property vector3d direction: Qt.vector3d(-1, Math.random() * 2 - 1, 0);
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    property var physicsSpeed
    property var serveTimer: 1

    onInitialize: {
        //Only the ball speed has to be reset during initalization
        //since the ball movement speeds up during gameplay
        physicsSpeed = speed;
    }

    onUpdate: {
        //Pause the ball movement before the serve
        if (hasPhysics && serveTimer > 0) {
            serveTimer -= getDeltaTime();
            return;
        }

        //Get the position of the script owner
        var transform = calculateGlobalTransform();
        var position = transform.row(3).toVector3d();

        //Check if ball is out of bounds
        var levelWidth = 600;
        if (position.x > levelWidth)
            fireEvent("onPlayerScore");
        else if (position.x < -levelWidth)
            fireEvent("onComputerScore");

        if (position.x < -levelWidth || position.x > levelWidth) {
            //Reset properties if out of bounds
            setAttribute("position.x", 0);
            setAttribute("position.y", 0);
            transform = calculateGlobalTransform();
            position = transform.row(3).toVector3d();
            direction.y = Math.random() * 2 - 1;
            physicsSpeed = speed;
            serveTimer = 1;
            return;
        }

        if (mouseDown) {
            //Handle movement with mouse input
77 78
            var perspectiveFactor = 1;
            var halfScreenHeight = 360;
79
            var target = (halfScreenHeight - getMousePosition().y) * perspectiveFactor;
80
            if (Math.abs(position.y - target) > 1) {
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
                if (position.y > target)
                    moveDown();
                else
                    moveUp();
            } else {
                stopVertical();
            }
        } else {
            stopVertical();
        }

        var next = Qt.vector3d(position.x, position.y, position.z);

        direction = direction.normalized();

        if (hasAI) {
            //Handle AI movement input
            var obstacleTransform = calculateGlobalTransform(obstacle1);
            var obstaclePosition = obstacleTransform.row(3).toVector3d();
100
            if (Math.abs(obstaclePosition.y - position.y) > 20)
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
                input.y = Math.sign(obstaclePosition.y - position.y);
            else
                input.y = 0;
        }

        //Separate collision solving to X and Y axis
        //Even if a collision happens on the X axis one can move along the Y axis and vice versa
        var canMoveX = true;
        var canMoveY = true;

        if (hasPhysics) {
            //Handle ball movement
            next.x += direction.x * physicsSpeed * getDeltaTime();
            next.y += direction.y * physicsSpeed * getDeltaTime();
            if (next.y > 300 || next.y < -300)
                canMoveY = false;
        } else {
            //Handle paddle movement
            next.y += input.y * speed * getDeltaTime();
        }

        //Check collisions against two objects
        var collisions = [collidesWith(obstacle1, position, next),
                          collidesWith(obstacle2, position, next)];

        var collisionPosition;
        for (var i = 0; i < collisions.length; ++i) {
            if (collisions[i].x || collisions[i].y)
                collisionPosition = collisions[i].position;
            if (collisions[i].x)
                canMoveX = false;
            if (collisions[i].y)
                canMoveY = false;
        }

        if (canMoveX) {
            setAttribute("position.x", next.x);
        } else {
            //Change ball direction and the angle if collision happens
            if (hasPhysics) {
141
                fireEvent("ballCollision");
142 143 144 145 146 147 148 149 150
                direction.x = -direction.x;
                direction.y = -Math.sin(Math.atan2(collisionPosition.y - position.y,
                                                   collisionPosition.x - position.x));
                physicsSpeed += 20;
            }
        }

        if (canMoveY) {
            setAttribute("position.y", next.y);
151
        } else {
152
            if (hasPhysics)
153
                fireEvent("ballCollision");
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
                direction.y = -direction.y;
        }
    }

    function collidesWith(obstacle, position, next) {
        if (obstacle === "")
            return {x: false, y: false};

        //Get obstacle position
        var obstacleTransform = calculateGlobalTransform(obstacle);
        var obstaclePosition = obstacleTransform.row(3).toVector3d();

        var ballSize = 50;
        var paddleWidth = 50;
        var paddleHeight = 100;

        var xCollides = true;
        var yCollides = true;

        //Rectangular collision check
        //All collisions are between the ball and a paddle so sizes can be hard-coded
        if (next.x + paddleWidth / 2 < obstaclePosition.x - ballSize / 2
            || next.x - paddleWidth / 2 > obstaclePosition.x + ballSize / 2
            || position.y + paddleHeight / 2 < obstaclePosition.y - ballSize / 2
            || position.y - paddleHeight / 2 > obstaclePosition.y + ballSize / 2) {
            xCollides = false;
        }

        if (position.x + paddleWidth / 2 < obstaclePosition.x - ballSize / 2
            || position.x - paddleWidth / 2 > obstaclePosition.x + ballSize / 2
            || next.y + paddleHeight / 2 < obstaclePosition.y - ballSize / 2
            || next.y - paddleHeight / 2 > obstaclePosition.y + ballSize / 2) {
            yCollides = false;
        }

        return {position: obstaclePosition, x: xCollides, y: yCollides};
    }

    function onMouseDown() {
        mouseDown = true;
    }

    function onMouseUp() {
        mouseDown = false;
    }

    function moveUp() {
        input.y = 1;
    }

    function moveDown() {
        input.y = -1;
    }

    function moveLeft() {
        input.x = -1;
    }

    function moveRight() {
        input.x = 1;
    }

    function stopHorizontal() {
        input.x = 0;
    }

    function stopVertical() {
        input.y = 0;
    }
}