Difference between revisions of "W1523 Paddle Paddle"

From Coder Merlin
m (Editorial review and minor corrections)
 
(17 intermediate revisions by 5 users not shown)
Line 1: Line 1:
[[File:Boy and girl play ping-pong, circa 1950 (4710047377).jpg|thumb|Boy and Girl Play Ping-Pong, circa 1950]]
[[File:Boy and girl play ping-pong, circa 1950 (4710047377).jpg|thumb|A boy and girl play ping-pong, circa 1950]]
== Prerequisites ==
== Prerequisites ==
* [[W1522 Ping Then Pong]]
* [[W1522 Ping Then Pong]]
Line 6: Line 6:


== Background ==
== Background ==
As we learned in the previous lab, the {{SwiftIdentifier|render}} event handler is invoked by the system periodically to refresh the canvas. We'll take advantage of this behavior to draw two "paddles", one on the left and one on the right. We'll also learn how to handle keyboard input.
As we learned in the previous lab, the {{SwiftIdentifier|render}} event handler is invoked by the system periodically to refresh the canvas. We'll take advantage of this behavior to draw two "paddles"—one on the left and one on the right. We'll also learn how to handle keyboard input.


== Experiment ==
== Experiment ==


=== Getting Started ===
=== Getting Started ===
Continue from the previous project; we'll be editing all of our files there. Enter into the Sources directory of the project.  
Continue from the previous project; we'll be editing all of our files there. Enter into the the project's Sources directory.
{{ConsoleLine|john-williams@codermerlin:|cd ~/Experiences/W1521/Sources/ScenesShell/}}
{{ConsoleLine|john-williams@codermerlin:|cd ~/Experiences/W1521/Sources/ScenesShell/}}


=== First Steps ===
=== First Steps ===
Let's start by adding a new file to our project, "Paddle.swift". Edit file "Paddle.swift":
Let's start by adding a new file to our project, {{Pathname|Paddle.swift}}. Open the file for editing.
<syntaxhighlight lang="bash">
emacs Paddle.swift
</syntaxhighlight>


=== Add Constructor and Required Methods ===
=== Add Constructor and Required Methods ===
Our paddle will be a rectangle. Feel free to use any colors for stroke and fill that you deem appropriate.
Our paddle will be a rectangle. Feel free to use any colors for stroke and fill that you deem appropriate.
<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
import Igis
import Igis
import Scenes


class Paddle {
class Paddle: RenderableEntity {
     var rectangle : Rectangle
     var rectangle: Rectangle


     init(topLeft:Point, size:Size) {
     init(rect:Rect) {
        let rect = Rect(topLeft:topLeft, size:size)
         rectangle = Rectangle(rect:rect, fillMode:.fillAndStroke)
         rectangle = Rectangle(rect:rect, fillMode:.fillAndStroke)
        // Using a meaningful name can be helpful for debugging
        super.init(name: "Paddle")
     }
     }


     func paint(canvas:Canvas) {
     override func render(canvas:Canvas) {
         let strokeStyle = StrokeStyle(color:Color(.black))
         let strokeStyle = StrokeStyle(color:Color(.black))
         let fillStyle = FillStyle(color:Color(.white))
         let fillStyle = FillStyle(color:Color(.white))
         let lineWidth = LineWidth(width:2)
         let lineWidth = LineWidth(width:2)
         canvas.paint(strokeStyle, fillStyle, lineWidth)
         canvas.render(strokeStyle, fillStyle, lineWidth, rectangle)
        canvas.paint(rectangle)
     }
     }


     func move(to:Point) {
     func move(to point:Point) {
         rectangle.rect.topLeft = to
         rectangle.rect.topLeft = point
     }
     }


Line 48: Line 47:
</syntaxhighlight>
</syntaxhighlight>


=== Add a Left Paddle to Our Painter ===
=== Add a Left Paddle to Our InteractionLayer ===
We'll need to make the following changes to our Painter class, so open "main.swift" and:
We'll need to make the following changes to our '''InteractionLayer''' class:
# Add a <code>paddleLeft</code> property
 
# Initialize <code>paddleLeft</code> in the constructor
# import the {{SwiftIdentifier|Igis}} library
# Set the initial coordinates of <code>paddleLeft</code>
# Add a {{SwiftIdentifier|paddleLeft}} property and initialize {{SwiftIdentifier|paddleLeft}}
# Paint <code>paddleLeft</code> in the <code>Painter.paint()</code> method
# Insert the {{SwiftIdentifier|paddleLeft}} into the {{SwiftIdentifier|InteractionLayer}}
# Set the initial coordinates of {{SwiftIdentifier|paddleLeft}} in a new, override func {{SwiftIdentifier|preSetup}}
 
<syntaxhighlight lang="swift" highlight="1">
    import Igis
</syntaxhighlight>


<syntaxhighlight lang="swift" highlight="2">
<syntaxhighlight lang="swift" highlight="2">
     let ball : Ball
     let ball = Ball()
     let paddleLeft : Paddle
     let paddleLeft = Paddle(rect:Rect(size:Size(width:10, height:100)))
    var coordinatesSet = false
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="swift" highlight="5">
<syntaxhighlight lang="swift" highlight="8">
     required init() {
     init() {
         ball = Ball(size:30)
         // Using a meaningful name can be helpful for debugging
         ball.changeVelocity(velocityX:10, velocityY:5)
         super.init(name:"Interaction")


         paddleLeft = Paddle(topLeft:Point(x:0, y:0), size:Size(width:10, height:100))
         insert(entity: ball, at: .front)
        ball.changeVelocity(velocityX: 3, velocityY: 5)
 
        insert(entity: paddleLeft, at: .front)
     }
     }
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="swift" highlight="7">
<syntaxhighlight lang="swift" highlight="7">
   func calculate(canvasSize:Size) {
   override func preSetup(canvasSize: Size, canvas: Canvas) {
         // Set the coordinates to the center of the screen if they've not yet been set
         paddleLeft.move(to:Point(x: 10, y: canvasSize.center.y))
        if !coordinatesSet {
  }
            let canvasCenter = Point(x:canvasSize.width/2, y:canvasSize.height/2)
</syntaxhighlight>
            ball.move(to:canvasCenter)


            paddleLeft.move(to:Point(x:10, y:canvasCenter.y))
{{RunProgram|Run the program and view in a browser before continuing. Ensure that the application behaves as expected.}}


            coordinatesSet = true
=== Add a Right Paddle to Our InteractionLayer ===
        }
Using what you've just learned, add a right paddle to the code.
        ball.calculate(canvasSize:canvasSize)
    }
</syntaxhighlight>


<syntaxhighlight lang="swift" highlight="5">
{{Observe|Section 1|
    func paint(canvas:Canvas, canvasSize:Size) {
Is it necessary to define another '''class''' to add another paddle? Why or why not?}}
        clearScreen(canvas:canvas, canvasSize:canvasSize)
        paintSkyAndGround(canvas:canvas, canvasSize:canvasSize)
        ball.paint(canvas:canvas)
        paddleLeft.paint(canvas:canvas)
    }
</syntaxhighlight>




[[File:Start button green arrow.svg|left|link=|Start button green arrow]] Run the project. <br>View the results in the browser. Ensure that the application behaves as expected.
{{RunProgram|Run the program and view in a browser before continuing. Ensure that the application behaves as expected and that a paddle is on the left of the screen and another is on the right.}}


=== Add an Event Handler to Process Key Down Events ===
Add the following method to our {{SwiftIdentifier|'''InteractionLayer'''}} class:


<syntaxhighlight lang="swift">
    func onKeyDown(key:String, code:String, ctrlKey:Bool, shiftKey:Bool, altKey:Bool, metaKey:Bool) {
    }
</syntaxhighlight>


=== Add a Right Paddle to Our Painter ===
Enable the onKeyDown handler by making the following changes:
Using what you've just learned, add a right Paddle to the code.


{{notice|[[File:Emblem-question-green.svg|frameless|30px]]|Question: Is it necessary to define another '''class''' in order to add another paddle?  Why or why not?}}
<syntaxhighlight lang="swift" highlight="1,8,11-13">
class InteractionLayer : Layer, KeyDownHandler


  ...


[[File:Start button green arrow.svg|left|link=|Start button green arrow]] Run the project. <br>View the results in the browser as you did earlier. Ensure that the application behaves as expected and that there is a paddle on the left of the screen and another on the right.
    override func preSetup(canvasSize: Size, canvas: Canvas) {
        paddleLeft.move(to:Point(x: 10, y: canvasSize.center.y))


        dispatcher.registerKeyDownHandler(handler: self)
    }


=== Add an Event Handler to Process Key Down Events ===
     override func postTeardown() {
Add the following method to our Painter class:
        dispatcher.unregisterKeyDownHandler(handler: self)
 
<syntaxhighlight lang="swift">
     override func onKeyDown(key:String, code:String, ctrlKey:Bool, shiftKey:Bool, altKey:Bool, metaKey:Bool) {
     }
     }
</syntaxhighlight>
</syntaxhighlight>
{{Hint|
Thoroughly review the [https://github.com/TheCoderMerlin/ScenesContainmentExample Scenes Containment Example]. How can '''containment''' help you provide the required functionality?
}}


== Exercises ==
== Exercises ==
# Use print statements to investigate the arguments provided to the <code>onKeyDown</code> event handler
{{W1523-Exercises}}
# Select sensible keys to be used to move the left paddle up and down and to move the right paddle up and down
* {{MMMAssignment|M1523-28}}
# Implement the required code to actually move the paddles in accordance with your selected keypresses


== Key Concepts ==
== Key Concepts ==
[[Category:IGIS]]

Latest revision as of 16:05, 25 May 2022

Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder
A boy and girl play ping-pong, circa 1950

Prerequisites[edit]

Research[edit]

Background[edit]

As we learned in the previous lab, the render event handler is invoked by the system periodically to refresh the canvas. We'll take advantage of this behavior to draw two "paddles"—one on the left and one on the right. We'll also learn how to handle keyboard input.

Experiment[edit]

Getting Started[edit]

Continue from the previous project; we'll be editing all of our files there. Enter into the the project's Sources directory.

john-williams@codermerlin: cd ~/Experiences/W1521/Sources/ScenesShell/

First Steps[edit]

Let's start by adding a new file to our project, Paddle.swift. Open the file for editing.

Add Constructor and Required Methods[edit]

Our paddle will be a rectangle. Feel free to use any colors for stroke and fill that you deem appropriate.

import Igis
import Scenes

class Paddle: RenderableEntity {
    var rectangle: Rectangle

    init(rect:Rect) {
        rectangle = Rectangle(rect:rect, fillMode:.fillAndStroke)

        // Using a meaningful name can be helpful for debugging
        super.init(name: "Paddle")
    }

    override func render(canvas:Canvas) {
        let strokeStyle = StrokeStyle(color:Color(.black))
        let fillStyle = FillStyle(color:Color(.white))
        let lineWidth = LineWidth(width:2)
        canvas.render(strokeStyle, fillStyle, lineWidth, rectangle)
    }

    func move(to point:Point) {
        rectangle.rect.topLeft = point
    }

}

Add a Left Paddle to Our InteractionLayer[edit]

We'll need to make the following changes to our InteractionLayer class:

  1. import the Igis library
  2. Add a paddleLeft property and initialize paddleLeft
  3. Insert the paddleLeft into the InteractionLayer
  4. Set the initial coordinates of paddleLeft in a new, override func preSetup
    import Igis
    let ball = Ball()
    let paddleLeft = Paddle(rect:Rect(size:Size(width:10, height:100)))
    init() {
        // Using a meaningful name can be helpful for debugging 
        super.init(name:"Interaction")

        insert(entity: ball, at: .front)
        ball.changeVelocity(velocityX: 3, velocityY: 5)

        insert(entity: paddleLeft, at: .front)
    }
   override func preSetup(canvasSize: Size, canvas: Canvas) {
        paddleLeft.move(to:Point(x: 10, y: canvasSize.center.y))
   }
Start button green arrow
Run the program and view in a browser before continuing. Ensure that the application behaves as expected.

Add a Right Paddle to Our InteractionLayer[edit]

Using what you've just learned, add a right paddle to the code.

ObserveObserveIcon.png
Observe, Ponder, and Journal: Section 1

Is it necessary to define another class to add another paddle? Why or why not?


Start button green arrow
Run the program and view in a browser before continuing. Ensure that the application behaves as expected and that a paddle is on the left of the screen and another is on the right.

Add an Event Handler to Process Key Down Events[edit]

Add the following method to our InteractionLayer class:

    func onKeyDown(key:String, code:String, ctrlKey:Bool, shiftKey:Bool, altKey:Bool, metaKey:Bool) {
    }

Enable the onKeyDown handler by making the following changes:

class InteractionLayer : Layer, KeyDownHandler

   ...

    override func preSetup(canvasSize: Size, canvas: Canvas) {
        paddleLeft.move(to:Point(x: 10, y: canvasSize.center.y))

        dispatcher.registerKeyDownHandler(handler: self)
    }

    override func postTeardown() {
        dispatcher.unregisterKeyDownHandler(handler: self)
    }
Hint.pngHelpful Hint

Thoroughly review the Scenes Containment Example. How can containment help you provide the required functionality?

Exercises[edit]

ExercisesExercisesIcon.png
  1. Use print statements to investigate the arguments provided to the onKeyDown event handler
  2. Select sensible keys to be used to move the left paddle up and down and to move the right paddle up and down
  3. Implement the required code to actually move the paddles in accordance with your selected keypresses
  •  M1523-28  Complete  Merlin Mission Manager  Mission M1523-28.

Key Concepts[edit]