Commit 7c762311 authored by Jere Tuliniemi's avatar Jere Tuliniemi

Remove TennisGame demo

Remove TennisGame demo since 2.0 doesn't support all he behavior
functionality needed by it.
parent 6e1336d4
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://qt.io/qt3dstudio/uia">
<assets initial="game">
<presentation id="game" src="game.uip"/>
</assets>
<statemachine ref="#logic">
<visual-states>
<state ref="Initial">
<enter>
<goto-slide element="main:Scene" rel="next"/>
</enter>
</state>
</visual-states>
</statemachine>
</application>
This diff is collapsed.
# Qt 3D Studio Tennis Game Demo
This demo application is created with Qt 3D Studio.
Behaviors and actions are used to implement a tennis game. The player is pitted against a computer opponent
with a mission of getting the ball behind the opponent to score. Whoever gets 3 points first wins.
The paddle of the player is controlled with a mouse by clicking on the screen and moving around.
Two custom behaviors are used: Movement.qml and Score.qml.
Movement.qml is the most complex script used. It handles the movement of the player, AI and the ball.
It includes collision detection and handles score detection and ball serving also.
Properties that can be set from the studio for Movement.qml are:
- Obstacle 1: An object that can be collided with
- Obstacle 2: Another object that can be collided with
- Has Physics: Determines if the object is handled as a ball
- Has AI: Determines if the object is handled as the AI paddle
The important handlers for Movement.qml are "onMouseDown" and "onMouseUp".
The Scene object in the presentation includes actions that listen for mouse input to the screen.
If such events occur the functions "onMouseDown" and "onMouseUp" from Movement.qml are called.
The script can then use the inputs to move the player around.
Movement.qml can also output two events: "onPlayerScore" and "onComputerScore".
The Movement script for the ball includes two actions in the studio that listen for these events.
When one side scores the functions that increment scores in the Score script, owned by the score text object, are called.
Score.qml itself also outputs two events: "onPlayerWins" and "onComputerWins".
The Score scripts increments the scores and, if one score is equal to 3, fires the win event for the owner of that score.
The actions that listen for the win events then tell the Scene object to jump to a victory or a defeat slide.
\ No newline at end of file
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*[[
<Property name="cameraTarget" formalName="Camera Target" type="ObjectRef" default="Scene.Layer.Camera" description="Object in scene the camera should look at" />
<Property name="startImmediately" formalName="Start Immediately?" type="Boolean" default="True" publishLevel="Advanced" description="Start immediately, or wait for\nthe Enable action to be called?" />
<Handler name="start" formalName="Start" category="CameraLookAt" description="Begin looking the target" />
<Handler name="stop" formalName="Stop" category="CameraLookAt" description="Stop looking the target" />
]]*/
import QtStudio3D.Behavior 1.0
Behavior {
//External:
property string cameraTarget
property bool startImmediately
//Internal:
property bool running: false
property var updateFunction
function start() {
running = true;
}
function stop() {
running = false;
}
onInitialize: {
if (startImmediately)
start();
}
onUpdate: {
if (!running)
return;
var targetTransform = calculateGlobalTransform(cameraTarget);
var targetSpot = targetTransform.row(3).toVector3d();
var cameraTransform = calculateGlobalTransform();
var cameraSpot = cameraTransform.row(3).toVector3d();
var matrix = calculateGlobalTransform(getParent()).inverted();
matrix.m41 = 0;
matrix.m42 = 0;
matrix.m43 = 0;
var rotateRay = targetSpot
.minus(cameraSpot)
.times(matrix);
var rotation = lookAt(rotateRay);
setAttributeVector("rotation", rotation);
setAttributeVector("position", cameraSpot);
}
function getAttributeVector(name) {
var vec = Qt.vector3d(0, 0, 0);
getAttribute(name + ".x", vec.x);
getAttribute(name + ".y", vec.y);
getAttribute(name + ".z", vec.z);
return vec;
}
function setAttributeVector(name, vec) {
setAttribute(name + ".x", vec.x);
setAttribute(name + ".y", vec.y);
setAttribute(name + ".z", vec.z);
}
}
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" ?>
<Effect>
<MetaData>
<!--Creates a corona around a sprite.-->
<Property name="HBlurBias" formalName="Horizontal Blur" min="0" max="10" default="2" description="Amount of corona horizontally."/>
<Property name="VBlurBias" formalName="Vertical Blur" min="0" max="10" default="2" description="Amount of corona vertically."/>
<Property name="Trailfade" formalName="Fade Amount" min="0" max="1" default="0.8" description="0=no glow\n0.5=fade quickly\n1.0=persistent trails"/>
<Property name="GlowSampler" filter="nearest" clamp="clamp" type="Texture" />
<Property name="GlowCol" formalName="Glow Color" type="Color" default="1 0.6 0.0" description="The color to use for the glow."/>
<Property name="NoiseSamp" formalName="Noise" filter="linear" clamp="repeat" default="./maps/effects/noise.dds" type="Texture" description="Texture to be used for the noise texture"/>
<Property name="NoiseScale" formalName="Noise Density" min="0" max="10" default="2" description="The density of the noise in corona.\nThe higher the value, the denser\nand smaller the noise;\nthe lower the value, the bigger the\nnoise scale."/>
<Property name="NoiseBright" formalName="Noise Brightness" min="0" max="5" default="4" description="Brightness of the noise."/>
<Property name="NoiseRatio" formalName="Noise Amount" min="0" max="1" default=".15" description="Magnitude of the noise."/>
<Property name="CrawlLen" formalName="Crawl Length" min="0" max="1" default=".3" description="Length of the corona trail in animation."/>
<Property name="CrawlAngle" formalName="Crawl Angle" default="0" description="Angle of the corona trail in animation."/>
<Property name="Sprite" filter="nearest" clamp="clamp" type="Texture" />
</MetaData>
<Shaders>
<Shared>
#include "blur.glsllib"
uniform float AppFrame; // frame number since app starts
uniform float FPS;
</Shared>
<Shader name="CORONA_HBLUR">
<Shared>
varying vec2 crawl; // corona crawl direction and magnitude
</Shared>
<VertexShader>
void vert ()
{
SetupHorizontalGaussianBlur(Texture0Info.x, HBlurBias, TexCoord);
// compute crawl
float alpha = radians(CrawlAngle + 180.0);
crawl = vec2(CrawlLen * sin(alpha), CrawlLen * cos(alpha));
}
</VertexShader>
<FragmentShader>
void frag()
{
//Passing in 1.0 means the value will not get alpha-multiplied again
float OutCol = GaussianAlphaBlur( GlowSampler, 1.0 );
OutCol *= Trailfade; // fade away glow color
OutCol += texture2D_0( TexCoord ).a; // add glow color in the original tex area
vec2 nuv = NoiseScale * TexCoord3 + AppFrame / FPS * crawl;
vec4 noise = texture2D_NoiseSamp(fract(nuv));
float ns = (1.0 - NoiseRatio) + NoiseRatio * NoiseBright * noise.x;
OutCol *= ns;
gl_FragColor = vec4( OutCol );
}
</FragmentShader>
</Shader>
<Shader name="CORONA_VBLUR">
<VertexShader>
void vert ()
{
SetupVerticalGaussianBlur( Texture0Info.y, VBlurBias, TexCoord );
}
</VertexShader>
<FragmentShader>
void frag() // PS_Blur_Vertical_9tap
{
float OutCol = GaussianAlphaBlur( Texture0, Texture0Info.z );
gl_FragColor = OutCol * vec4(GlowCol, 1.0);
}
</FragmentShader>
</Shader>
<Shader name="CORONA_BLEND">
<VertexShader>
void vert()
{
}
</VertexShader>
<FragmentShader>
void frag ()
{
vec4 src = texture2D_0( TexCoord );
vec4 dst = texture2D_Sprite(TexCoord);
colorOutput( src * (1.0 - dst.a) + dst );
}
</FragmentShader>
</Shader>
</Shaders>
<Passes>
<Buffer name="glow_buffer" type="ubyte" format="rgba" filter="linear" wrap="clamp" size="0.55" lifetime="scene"/>
<Buffer name="temp_buffer" type="ubyte" format="rgba" filter="linear" wrap="clamp" size="0.55" lifetime="frame"/>
<Pass shader="CORONA_HBLUR" input="[source]" output="temp_buffer">
<BufferInput param="GlowSampler" value="glow_buffer"/>
</Pass>
<Pass shader="CORONA_VBLUR" input="temp_buffer" output="glow_buffer"/>
<Pass shader="CORONA_BLEND" input="glow_buffer">
<BufferInput param="Sprite" value="[source]"/>
</Pass>
</Passes>
</Effect>
<?xml version="1.0" encoding="UTF-8" ?>
<Effect>
<MetaData>
<!--Smooth, unchanging areas of the image are turned black, while areas of the image with sharp color changes are brightened to highlight the 'edges' in the image.-->
<Property name="amount" formalName="Amount" min="0" max="1" default="1" description="0 = no effect\n1 = maximum effect"/>
</MetaData>
<Shaders>
<Shared>
////////////////////////////////////////////////////////////
// vert / frag shaders.
varying vec4 TexCoordBLL; //Bottom Left and Bottom Tap
varying vec4 TexCoordTLT; //Top Left and Top Tap
varying vec4 TexCoordTRR; //Upper Right and Right Tap
varying vec4 TexCoordBRB; //Bottom Right and Bottom Tap
</Shared>
<Shader>
<VertexShader>
void vert()
{
//Pass Through the Texture Taps
float deltax = 1.0/Texture0Info.x;
float deltay = 1.0/Texture0Info.y;
//Bottom Left
TexCoordBLL.x = TexCoord.s - deltax;
TexCoordBLL.y = TexCoord.t - deltay;
//Left
TexCoordBLL.z = TexCoord.s - deltax;
TexCoordBLL.w = TexCoord.t;
//Top Left
TexCoordTLT.x = TexCoord.s - deltax;
TexCoordTLT.y = TexCoord.t + deltay;
//Top
TexCoordTLT.z = TexCoord.s;
TexCoordTLT.w = TexCoord.t + deltay;
//Upper Right
TexCoordTRR.x = TexCoord.s + deltax;
TexCoordTRR.y = TexCoord.t + deltay;
//Right
TexCoordTRR.z = TexCoord.s + deltax;
TexCoordTRR.w = TexCoord.t;
//Bottom Right
TexCoordBRB.x = TexCoord.s + deltax;
TexCoordBRB.y = TexCoord.t - deltay;
//Bottom
TexCoordBRB.z = TexCoord.s;
TexCoordBRB.w = TexCoord.t - deltay;
}
</VertexShader>
<FragmentShader>
void frag (void)
{
vec4 centerTap = texture2D_0(TexCoord);
vec4 edgeTap = texture2D_0(TexCoordBLL.xy) +
texture2D_0(TexCoordBLL.zw) +
texture2D_0(TexCoordTLT.xy) +
texture2D_0(TexCoordTLT.zw) +
texture2D_0(TexCoordTRR.xy) +
texture2D_0(TexCoordTRR.zw) +
texture2D_0(TexCoordBRB.xy) +
texture2D_0(TexCoordBRB.zw);
vec3 edgeDetect = 8.0*(centerTap.rgb + -0.125*edgeTap.rgb);
edgeDetect = clamp(edgeDetect, 0.0, centerTap.a);
colorOutput(vec4(mix(centerTap.rgb, edgeDetect, amount), centerTap.a));
}
</FragmentShader>
</Shader>
</Shaders>
</Effect>
This diff is collapsed.
/*[[
<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" />
<Event name="ballCollision" />
]]*/
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
property bool mouseDown: false
property vector3d input: Qt.vector3d(0, 0, 0);
property vector3d direction: Qt.vector3d(-1, Math.random() * 2 - 1, 0);
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
var perspectiveFactor = 1;
var halfScreenHeight = 360;
var target = (halfScreenHeight - getMousePosition().y) * perspectiveFactor;
if (Math.abs(position.y - target) > 1) {
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();
if (Math.abs(obstaclePosition.y - position.y) > 20)
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) {
fireEvent("ballCollision");
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);
} else {
if (hasPhysics)
fireEvent("ballCollision");
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;
}
}
/*[[
<Property name="isplayerScore" formalName="Player Score" animatable="False" type="Boolean" default="False" />
<Handler name="addPlayerScore" formalName="Add Player Score" />
<Handler name="addComputerScore" formalName="Add Computer Score" />
<Handler name="resetScores" formalName="Reset Scores" />
<Event name="onPlayerWins" />
<Event name="onComputerWins"/>
]]*/
import QtStudio3D.Behavior 1.0
Behavior {
property int playerScore: 0
property int computerScore: 0
property bool isplayerScore: false
onUpdate: {
if (isplayerScore === true)
setAttribute("textstring", playerScore.toFixed());
else
setAttribute("textstring", computerScore.toFixed());
}
function addPlayerScore() {
playerScore++;
if (playerScore >= 3)
fireEvent("onPlayerWins");
}
function addComputerScore() {
computerScore++;
if (computerScore >= 3)
fireEvent("onComputerWins");
}
function resetScores() {
playerScore = 0;
computerScore = 0;
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<Effect>
<MetaData>
<Property name="vig_color" formalName="Vignet Color" type="Color" default="0.5 0.5 0.5" description="The color to use for the Vignetting."/>
<Property name="vig_strenght" formalName="Strenght" min="0" max="15" default="15.0" description="Strenght of Vignetting."/>
<Property name="vig_radius" formalName="Radius" min="0" max="5" default="0.35" description="Size of Vignetting."/>
</MetaData>
<Shaders>
<Shared></Shared>
<VertexShaderShared></VertexShaderShared>
<FragmentShaderShared></FragmentShaderShared>
<Shader name="main">
<VertexShader></VertexShader>
<FragmentShader><![CDATA[
void frag()
{
float radius = vig_radius;
vec4 origColor = texture2D_0(TexCoord);
vec2 uv = TexCoord.xy;
vec2 center = vec2(0.5);
vec4 bg = origColor;
uv *= 1.0 - uv.yx;
float vig = uv.x*uv.y * vig_strenght;
vig = pow(vig, vig_radius);
vec4 vigmixcolor = vec4(vig_color,vig) * vec4(vig);
gl_FragColor = vec4(mix(origColor,vigmixcolor,1.0-vig));
}
]]></FragmentShader>
</Shader>
</Shaders>
<Passes>
<Pass shader="main" input="[source]" output="[dest]"/>
</Passes>
</Effect>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment