BallTrajectory
Educational motion graphic developed in Smart Mobile Studio by Felix Thompson: Y13 Age ~18
Introduction
Felix shows how to use Physics to make the ball move in a realistic manner. (Sometimes the implementation of the effect of gravity uses a technique that is chosen for speed of execution rather than for strict adherence to the laws of nature). As usual, Felix comments the code thoroughly for your benefit.
The code follows the graphic in action. Refresh the screen for a different output; the ball will restart from the centre of the box with a different random velocity.
See also the use of much of the code in the Pixi.js version below and in the form-based Compendium.
If BallTrajectory does not run in your current browser, please try another (such as Chrome). If you see no display at school, the security system might have blocked it. You can try instead this direct link to the program running on its own page.
The Code
unit BallTrajectory; { Copyright (c) 2014 Felix Thompson Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License, as described at http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ } interface uses W3System, W3Components, W3Application, W3Game, W3GameApp, W3Graphics; type TApplication = class(TW3CustomGameApplication) protected procedure ApplicationStarting; override; procedure ApplicationClosing; override; procedure PaintView(Canvas: TW3Canvas); override; end; implementation { TApplication} var t : Integer; mX, mY, vX, vY : Real; procedure TApplication.ApplicationStarting; begin inherited; GameView.Delay := 20; GameView.StartSession(True); mX := 150; // The ball starts in the centre of the mY := 150; // screen after each refresh. vX := (100 - RandomInt(200)); // A random vertical and vY := 20* (10 - RandomInt(20)); // horizontal velocity is chosen. end; procedure TApplication.ApplicationClosing; begin GameView.EndSession; inherited; end; procedure TApplication.PaintView(Canvas: TW3Canvas); begin Canvas.FillStyle := 'white'; Canvas.FillRectF(0, 0, GameView.Width, GameView.Height); //The screen in drawn with edges. Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 299 , 300, 1); Canvas.FillStyle := 'Black'; Canvas.FillRectF(299, 0, 1, 300); Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 0 , 300, 1); Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 0, 1, 300); t += 1; vY := vY - 0.2 * t; //Vertical velocity always increases downwards - gravity. mY -= 0.1 * vY; //The speeds are added to the current location mX -= 0.1 * vX; //to give the new position. if mY + 10 >= 300 then //This stops the ball from going underground, begin //it reverses its direction on collision and vy := -vY * 0.9; //loses a factor of its speed - restitution. mY := 290; end; if mY - 10 <= 0 then begin vy := -vY * 0.9; mY := 10; end; if (mX + 10 >= 300) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 290; end; //The same happens here but for the walls. if (mX - 10 <= 0) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 10; end; //If the ball is on the ground it loses if (mY = 290) then //horizontal speed - friction. begin vX := vX * 0.95; end; Canvas.FillStyle := 'Black'; //The ball is drawn Canvas.BeginPath; Canvas.Ellipse(Round(mX) + 10, Round(mY) + 10, Round(mX) -10, Round(mY) - 10); Canvas.Fill; Canvas.ClosePath; end; end.
BallTrajectory Project File for Version 3 of Smart Mobile Studio
You can compile this trimmed-down project file (without any warnings) using the command-line compiler of Version 3 of Smart Mobile Studio.
<SMART> <Project version="3" subversion="0"> <Name>BallTrajectory</Name> <Author>Felix Thompson</Author> <Company>PPS</Company> <Options> <Compiler /> <Codegen> <Obfuscation>1</Obfuscation> <Devirtualize>1</Devirtualize> <MainBody>1</MainBody> <CodePacking>1</CodePacking> <SmartLinking>1</SmartLinking> </Codegen> <ConditionalDefines /> <Linker> <EmbedJavaScript>1</EmbedJavaScript> </Linker> <Output> <HtmlFileName>BallTrajectory.html</HtmlFileName> <OutputFilePath>www\</OutputFilePath> </Output> <Import /> <Execute /> </Options> <Files> <File type="main"> <Name>BallTrajectoryApplication</Name> <Source> <![CDATA[uses SmartCL.System, BallTrajectory; var Application: TApplication; try Application := TApplication.Create; Application.RunApp; except on e: Exception do ShowMessage(e.message); end; ]]> </Source> </File> <File type="unit"> <Name>BallTrajectory</Name> <Source> <![CDATA[unit BallTrajectory; interface uses SmartCL.System, SmartCL.Components, SmartCL.Application, SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics; type TApplication = class(TW3CustomGameApplication) protected procedure ApplicationStarting; override; procedure ApplicationClosing; override; procedure PaintView(Canvas: TW3Canvas); override; end; implementation var t : Integer; mX, mY, vX, vY : Float; procedure TApplication.ApplicationStarting; begin inherited; GameView.Delay := 20; GameView.StartSession(True); mX := 150; // The ball starts in the centre of the mY := 150; // screen after each refresh. vX := (100 - RandomInt(200)); // A random vertical and vY := 20* (10 - RandomInt(20)); // horizontal velocity is chosen. end; procedure TApplication.ApplicationClosing; begin GameView.EndSession; inherited; end; procedure TApplication.PaintView(Canvas: TW3Canvas); begin Canvas.FillStyle := 'white'; Canvas.FillRectF(0, 0, GameView.Width, GameView.Height); //The screen in drawn with edges. Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 299 , 300, 1); Canvas.FillStyle := 'Black'; Canvas.FillRectF(299, 0, 1, 300); Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 0 , 300, 1); Canvas.FillStyle := 'Black'; Canvas.FillRectF(0, 0, 1, 300); t += 1; vY := vY - 0.2 * t; // Vertical velocity always increases downwards - gravity. mY -= 0.1 * vY; // The speeds are added to the current location mX -= 0.1 * vX; // to give the new position. if mY + 10 >= 300 then // This stops the ball from going underground, begin // it reverses its direction on collision and vy := -vY * 0.9; // loses a factor of its speed - restitution. mY := 290; end; if mY - 10 <= 0 then begin vy := -vY * 0.9; mY := 10; end; if (mX + 10 >= 300) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 290; end; // The same happens here but for the walls. if (mX - 10 <= 0) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 10; end; // If the ball is on the ground it loses if (mY = 290) then // horizontal speed - friction. begin vX := vX * 0.95; end; Canvas.FillStyle := 'Black'; // The ball is drawn Canvas.BeginPath; Canvas.Ellipse(Round(mX) + 10, Round(mY) + 10, Round(mX) -10, Round(mY) - 10); Canvas.Fill; Canvas.ClosePath; end; end. ]]> </Source> </File> </Files> <Target>Browser</Target> <Generator>Canvas Project</Generator> </Project> </SMART>
BallTrajectory with Rendering by Pixi.js
Change the code in the demo Projects\Featured Demos\Games\PixiJS Renderer\Graphics\Graphics.spr supplied with Version 2.2 RC of Smart Mobile Studio to the following for an example of fast rendering.
{ Copyright (c) 2014 Felix Thompson Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License, as described at http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ Converted for Pixi.js rendering by PPS (2016) } uses W3C.HTML5, W3C.AnimationTiming, W3C.HighResolutionTime, PixiJS; var t : Integer; var mX, mY, vX, vY : Float; var Renderer := JSystemRenderer(JPIXI.AutoDetectRenderer(300, 300, JRendererOptions(class antialias = true end))); document.body.appendChild(Renderer.view); var Stage := new JContainer; var Graphics := new JGraphics; Stage.addChild(Graphics); mX := 150; // The ball starts in the centre of the mY := 150; // screen after each refresh. vX := (100 - RandomInt(200)); // A random vertical and vY := 20 * (10 - RandomInt(20)); // horizontal velocity is chosen. procedure Animate(Time: TDOMHighResTimeStamp); begin requestAnimationFrame(Animate); t += 1; vY := vY - 0.2 * t; // Vertical velocity always increases downwards - gravity. mY -= 0.1 * vY; // The speeds are added to the current location mX -= 0.1 * vX; // to give the new position. if mY + 10 >= 300 then // This stops the ball from going underground, begin // it reverses its direction on collision and vy := -vY * 0.9; // loses a factor of its speed - restitution. mY := 290; end; if mY - 10 <= 0 then begin vy := -vY * 0.9; mY := 10; end; if (mX + 10 >= 300) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 290; end; // The same happens here but for the walls. if (mX - 10 <= 0) AND (Abs(vX) > 0.01) then begin vX := -vX * 0.9; mX := 10; end; // If the ball is on the ground it loses if (mY = 290) then // horizontal speed - friction. begin vX := vX * 0.95; end; // Draw white background with black edges Graphics.lineStyle(2, $000000, 1); Graphics.beginFill($FFFFFF, 1); Graphics.drawRect(0, 0, 300, 300); Graphics.endFill; // Draw circle with lineStyle set to zero so circle has no outline Graphics.lineStyle(0); Graphics.beginFill($000000, 1); Graphics.drawCircle(mX, mY, 10); Graphics.endFill; Renderer.render(Stage); end; requestAnimationFrame(Animate);