Wednesday, November 13, 2013

Photoshop: The Gondola

Assignment:  We have accepted an assignment from a client to take a dull, scanned photograph and improve it for use in a poster, brochure, web site, etc.  The client has scanned a photograph of a gondola, but they think it is too hazy... and they don't like the speedboat in the center of the picture... and "the colors are a little blah".  We will use PhotoShop to correct these issues... and more.

Photoshop Tools/Options Used:
  • Ruler
  • Rotate Canvas
  • Crop
  • Levels
  • Clone Stamp
  • Dodge
  • Burn
  • Replace Color
  • Magic Wand
  • Paste Into
  • Free Transform

We will be using the following images to create our final project:


Part I: We will begin with the Gondola picture:
  • Copy the Gondola picture
  • Do File > New and click OK
  • Select VIEW > FIT ON SCREEN (shortcut is CTRL+0) to see the image as large as possible
  • Select the RULER tool (sometimes it's hidden under the EYEDROPPER) -- a shortcut is SHIFT+I which toggles through that toolset.
  • Drag the ruler along an edge that should be straight (i.e. the edge of the tower).
  • Select IMAGE > ROTATE CANVAS > ARBITRARY...
  • A suggested rotation is already in place based on your ruler line (this tool is trying to make the ruler line vertical or horizonal)... so click OK.
  • Use the RECTANGULAR MARQUEE tool (the box shape) to select as much of the picture as possible without including the black border (it's okay if you don't get all of the photo in the square).
  • Select IMAGE > CROP
  • Select VIEW > FIT ON SCREEN (again, to see the maximum image size)
  • Select IMAGES > ADJUSTMENTS > LEVELS
  • Drag the "black triangle" in toward the very beginning of the "mountain range" -- and do the same for the "white triangle" (click the "Preview" box to toggle this change to preview what it is doing) then click OK.
  • You now have a brightened, cropped image to work with for the next part.
Part II: Removing the Motor Boat / Using the Clone Stamp
  • Click the CLONE STAMP tool
  • Move your pointer over to a clear area of water -- about the same distance/depth as your motorboat (i.e. somewhere in the same horizontal line) and ALT+CLICK.
  • Move your mouse over to the center of the motorboat and slowly paint over the boat.  Notice the "+" shape where the CLONE STAMP is copying from.
Part III: Adjusting Highlights and Shadows
  • Click the DODGE tool (it looks like a black pushpin) and set the Range for HIGHLIGHTS and the Exposure to around 10%.
  • Adjust your brush size as necessary with the [ and ] keys.
  • Brush over the red and white striped tarp and notice how the highlights are getting brighter.  Try this on other tarps and the side of the boat which seems to get shinier.
  • Click the BURN tool (it looks like a hand pinching and is found under the DODGE tool) and set the Range for SHADOWS and the Exposure to around 5%.
  • Adjust your brush size as necessary with the [ and ] keys.
  • Brush over the boats/tarps just to get a little contrast.
Part IV: Replace the Orange Tarp
  • Use the ZOOM tool (it looks like a magnifying glass) to zoom in on the orange tarp -- making sure you can see the entire thing.
  • Use the LASSO tool and draw around the orange tarp (making a selection)
  • Select IMAGE > ADJUSTMENT > REPLACE COLOR
  • Click on the orange part of the orange tarp.  You should see a faint "mask" of the orange shape appear in the Replace Color box.
  • Hold down the SHIFT key and Click and Drag around the orange tarp trying to select ONLY the orange areas.  If you over-select (i.e. you accidentally go into the black) you can start this step over.
  • When you feel like you have a nice, sharp "mask" of the orange tarps shape, drag the HUE slider left and right.  Notice the colors changing on your image.
  • If you adjust the SATURATION slider and the LIGHTNESS slider, you can actually match the colors of the other tarps on the boat.
Part V: Make Blue Skies From Gray
  • Copy the Clouds picture.
  • Select EDIT > COPY (or do CTRL+C)
  • Go back to your Gondola picture.
  • Click the MAGIC WAND tool (SHIFT+W toggles this)
  • Click in one of the "gray" areas of the sky and notice how it selects some of the building tops.  We don't want that.
  • Drop your TOLERANCE down to 10 and see how that works.  Notice that it [probably] doesn't select the entire piece.  We can compensate for that by holding SHIFT and clicking in the "unselected" areas.
  • Continue SHIFT-Clicking the other "gray" areas of the sky throughout the picture.
  • Select EDIT > PASTE INTO (not PASTE)
  • Notice how ugly and unnatural this looks?  Don't worry -- go over to the OPACITY setting (above your LAYERS) and drag that down until it looks natural.  Mine is about 37% [this time].
  • When you are satisfied with your final product, select LAYER > FLATTEN IMAGE.
Part VI: Make the Before and After
  • Go to IMAGE > CANVAS SIZE
  • Change "inches" to percent and change the width (since this is a picture that is taller than it is wide, a side-by-side comparison works best -- so we will adjust the width) and change the 100 to 200 (making it twice the width).
  • Click on the Right Arrow (this means you want your current picture -- the "after" -- to be on the right) and click OK.
  • Copy your original image (in this case, p_start.psd) and Paste it into your finished image.  Use the MOVE tool (the black arrow) to drag it to the left side.
  • You may want to use your FREE TRANSFORM to rotate and resize the original so it fits better.
  • Add your name to the finished image.
  • Save your completed file as a JPEG image to your Thawspace. 
Part VII: Add to Your Portfolio
  • Go to your Blog (http://www.blogger.com/) and sign into your account.
  • Click New Post and make sure "Compose" is selected.
  • Click the IMAGE/PICTURE button.
  • Browse for your picture (from your Thawspace).
  • Add a description about the steps you completed or the process you used in putting this together.  You could even add the reason you did the project (e.g. A virtual client wanted you to clean up an image to use for print/online media).
  • Click PUBLISH POST.
  • Click VIEW BLOG.
If you have difficulty following along and would like to work on this from home, here is a tutorial using the same images:


Tuesday, November 12, 2013

Begin Computer Graphics: Veggie Heads

Something we should probably have covered earlier in the year is graphic editing and creation.  For this class we will begin with Adobe Photoshop, although we may explore Illustrator, Flash, or even a 3D graphics application at some point this year.

Today we will explore some of the basic Photoshop tools and concepts, and then we will begin our first official Photoshop project -- the Veggie Head.  The Veggie Head project is a classic Photoshop tutorial which teaches you how to use a variety of tools and techniques to accomplish a seemingly easy task.
The task for today:
  1. Right-click the image above and select "Copy Image" (or "Copy Picture" depending on your browser)
  2. Open Adobe Photoshop
  3. Go to FILE > NEW (notice the size of the new image is the same size as the image on your "clipboard")
  4. Go to FILE > PASTE (you should now have two layers -- the veggies and a white background)
  5. Your task is to take the various pieces, copy them, paste them, and clean them up to create a "Veggie Head".  Some examples can be seen below:


Friday, November 8, 2013

Modifying Pong to Speed Up

This is an example of making the ball speed up when you hit it with the paddle each time:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * This is the Ball for our Pong game.
 * @Robert Griffith 
 * @11/05/2013
 */
public class Ball extends Actor
{
    double x = 320; // (getWorld().getWidth())/2;  // Half my world width
    double y = 240; // (getWorld().getHeight())/2; // Half my world height
    double yDirection;
    double xDirection;
    double ballSpeedStart = 2.000;
    double ballSpeed = ballSpeedStart;
    double speedIncrease = 0.001;
    int score1 = 0;
    int score2 = 0;
    int start = 0;

    // Cycle through the main program.
    public void act() 
    {
        if (start == 0){ startup(); }
        paddleBounce(); // Test for paddle bounce
        edgeBounce(); // Test for edge bounce
        x = x + xDirection;
        y = y + yDirection;
        int x1 = (int) x;
        int y1 = (int) y;
        setLocation(x1, y1);
    } 
    // Set the direction and speed of the Ball -- randomly at first.
    public void startup(){  
        start++;
        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { yDirection = -1*ballSpeedStart; } 
        else { yDirection = ballSpeedStart; }
        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { xDirection = -1*ballSpeedStart; } 
        else { xDirection = ballSpeedStart; }
    }
    // If we reach the edge of the world, bounce.
    public void edgeBounce()
    {
        if (atWorldEdge())
        {
            Greenfoot.playSound("4389__noisecollector__pongblipf-3.wav");
            if (x > 640) // Give Player1 Score & Launch Another Ball
            { 
                score1++;
                x = (getWorld().getWidth())/2;
                y = (getWorld().getHeight())/2;
                ballSpeed = ballSpeedStart;
                start = 0;
            }
            if (x < 0) // Give Player2 Score & Launch Another Ball
            { 
                score2++;
                x = (getWorld().getWidth())/2;
                y = (getWorld().getHeight())/2;
                ballSpeed = ballSpeedStart;
                start = 0;
            }
            if (y < 0 || y > getWorld().getHeight()) {yDirection = yDirection * -1;} // Bounce off roof or floor.
        }
    }
    // If the ball hits either paddle, reverse the direction of the ball.
    public boolean paddleBounce()
    {
        if (canSee(Paddle1.class))
        {
            Greenfoot.playSound("4391__noisecollector__pongblipf-5.wav");
            ballSpeed = ballSpeed + speedIncrease;
            xDirection = xDirection - ballSpeed;
            xDirection = (xDirection) * (-1);

        }
        if (canSee(Paddle2.class))
        {
            Greenfoot.playSound("4391__noisecollector__pongblipf-5.wav");
            ballSpeed = ballSpeed + speedIncrease;
            xDirection = xDirection + ballSpeed;
            xDirection = (xDirection) * (-1);
        }
        return false;
    }
    // Check to see if we can see an object.
    public boolean canSee(Class clss)  
    {  
        Actor actor = getOneObjectAtOffset(0, 0, clss);  
        return actor != null;          
    }  
    // Test if we are close to one of the edges of the world. Return true if we are.
    public boolean atWorldEdge()
    {
        if(getX() < 10 || getX() > getWorld().getWidth() - 10)
            return true;
        if(getY() < 10 || getY() > getWorld().getHeight() - 10)
            return true;
        else
            return false;
    }
    // Holds score1 variable to be called by the Scores class for display.
    public int getScore1()
    {return score1;}  
    // Holds score2 variable to be called by the Scores class for display.
    public int getScore2() // The Scores class reads this for display
    {return score2;}  
}

Thursday, November 7, 2013

Pong: Fully Operational

My "final" version of Pong includes some changes to the code (including a ball that speeds up the more it's hit by the paddles) and some updates to the Score Counter.

Let's begin with the "Board" class (which is my "world"):
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * The Board class is my world.
 * @Robert Griffith
 * @11/05/2013
 */
public class Board extends World
{
    public Board()
    {    
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(640, 480, 1); 
        prepare();
    }
    private void prepare()
    {
        Ball ball = new Ball();
        addObject(ball, 320, 240);
        
        Scores scores = new Scores();
        addObject(scores, 320, 20);
        
        Paddle1 paddle1 = new Paddle1();
        addObject(paddle1, 10, 240);
        Paddle2 paddle2 = new Paddle2();
        addObject(paddle2, 630, 240);

    }
}
Next we will add the "Scores" class (a subclass of Actor):
import greenfoot.*;
import java.awt.Color;

/**
 * Score Display using  "getObjects" and java.awt.Color to write on screen.
 */
public class Scores extends Actor
{
    int maxScore = 10;
    int score1 = 0;
    int score2 = 0;

    public void act()   
    {    
        if (!getWorld().getObjects(Ball.class).isEmpty())   
        {    
            int score1 = ((Ball) getWorld().getObjects(Ball.class).get(0)).getScore1(); 
            int score2 = ((Ball) getWorld().getObjects(Ball.class).get(0)).getScore2(); 
            setImage(new GreenfootImage(" Player One: " + score1 + "              Player Two: "+score2+" ", 24, Color.white, Color.black));
            if (score1 == maxScore)
            {
                setImage(new GreenfootImage(" Player 1 Wins! ", 48, Color.white, Color.black));
                Greenfoot.stop();
            }
            if (score2 == maxScore)
            {
                setImage(new GreenfootImage(" Player 2 Wins! ", 48, Color.white, Color.black));
                Greenfoot.stop();
            }}    
    }    
}
Then the "Ball" class -- which does most of the work in the program:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * This is the Ball for our Pong game.
 * @Robert Griffith 
 * @11/05/2013
 */
public class Ball extends Actor
{
    double x = 320; // (getWorld().getWidth())/2;  // Half my world width
    double y = 240; // (getWorld().getHeight())/2; // Half my world height
    double yDirection;
    double xDirection;
    double ballSpeedStart = 2.000;
    double ballSpeed = ballSpeedStart;
    double speedIncrease = 0.001;
    int score1 = 0;
    int score2 = 0;
    int start = 0;

    // Cycle through the main program.
    public void act() 
    {
        if (start == 0){ startup(); }
        paddleBounce(); // Test for paddle bounce
        edgeBounce(); // Test for edge bounce
        x = x + xDirection;
        y = y + yDirection;
        int x1 = (int) x;
        int y1 = (int) y;
        setLocation(x1, y1);
    } 
    // Set the direction and speed of the Ball -- randomly at first.
    public void startup(){  
        start++;
        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { yDirection = -1*ballSpeedStart; } 
        else { yDirection = ballSpeedStart; }
        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { xDirection = -1*ballSpeedStart; } 
        else { xDirection = ballSpeedStart; }
    }
    // If we reach the edge of the world, bounce.
    public void edgeBounce()
    {
        if (atWorldEdge())
        {
            Greenfoot.playSound("4389__noisecollector__pongblipf-3.wav");
            if (x > 640) // Give Player1 Score & Launch Another Ball
            { 
                score1++;
                x = (getWorld().getWidth())/2;
                y = (getWorld().getHeight())/2;
                ballSpeed = ballSpeedStart;
                start = 0;
            }
            if (x < 0) // Give Player2 Score & Launch Another Ball
            { 
                score2++;
                x = (getWorld().getWidth())/2;
                y = (getWorld().getHeight())/2;
                ballSpeed = ballSpeedStart;
                start = 0;
            }
            if (y < 0 || y > getWorld().getHeight()) {yDirection = yDirection * -1;} // Bounce off roof or floor.
        }
    }
    // If the ball hits either paddle, reverse the direction of the ball.
    public boolean paddleBounce()
    {
        if (canSee(Paddle1.class))
        {
            Greenfoot.playSound("4391__noisecollector__pongblipf-5.wav");
            ballSpeed = ballSpeed + speedIncrease;
            xDirection = xDirection - ballSpeed;
            xDirection = (xDirection) * (-1);

        }
        if (canSee(Paddle2.class))
        {
            Greenfoot.playSound("4391__noisecollector__pongblipf-5.wav");
            ballSpeed = ballSpeed + speedIncrease;
            xDirection = xDirection + ballSpeed;
            xDirection = (xDirection) * (-1);
        }
        return false;
    }
    // Check to see if we can see an object.
    public boolean canSee(Class clss)  
    {  
        Actor actor = getOneObjectAtOffset(0, 0, clss);  
        return actor != null;          
    }  
    // Test if we are close to one of the edges of the world. Return true if we are.
    public boolean atWorldEdge()
    {
        if(getX() < 10 || getX() > getWorld().getWidth() - 10)
            return true;
        if(getY() < 10 || getY() > getWorld().getHeight() - 10)
            return true;
        else
            return false;
    }
    // Holds score1 variable to be called by the Scores class for display.
    public int getScore1()
    {return score1;}  
    // Holds score2 variable to be called by the Scores class for display.
    public int getScore2() // The Scores class reads this for display
    {return score2;}  
}
Finally, we will implement the paddles. First, Paddle1:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * The left paddle (Paddle1) class.
 * @Robert Griffith
 * @11/05/213
 */
public class Paddle1 extends Actor
{
    int y = 240;
    int paddleSpeed = 10;

    /**
     * Run the program.
     */
    public void act() 
    {
        checkKeys();
    }    

    /**
     *  Check keys and move the paddle
     */
    public void checkKeys()
    {
        if ( Greenfoot.isKeyDown("a"))
        {
            y=y-paddleSpeed;
            if (y < 48) {y = 48; } // y = half my paddle height
            setLocation(10, y); // Set the x coordinate at 10
        }
        if ( Greenfoot.isKeyDown("z"))
        {
            y=y+paddleSpeed;
            if (y > 432) {y = 432; } // y = world width - half my paddle height
            setLocation(10, y); // Set the x coordinate at 10
        }
    }
}
And Paddle2 -- which is nearly identical to Paddle1:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class Paddle1 here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Paddle2 extends Actor
{
    int y = 240;
    int paddleSpeed = 10;
    
    
    /**
     * Act - do whatever the Paddle1 wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act() 
    {
        checkKeys();
    }    
    
    /**
     *  Check keys and move the paddle
     */
    public void checkKeys()
    {
        if ( Greenfoot.isKeyDown("up"))
        {
            y=y-paddleSpeed;
            if (y < 48) {y = 48; }
            setLocation(630, y);
        }
         if ( Greenfoot.isKeyDown("down"))
        {
            y=y+paddleSpeed;
            if (y > 432) {y = 432; }
            setLocation(630, y);
        }
       
    }
}

Tuesday, November 5, 2013

Pong Continued: Moving Ball and Paddles

To begin with, here is the "Board" class code (my world):
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * The Board class is my world.
 * @Robert Griffith
 * @11/05/2013
 */
public class Board extends World
{
    public Board()
    {    
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(640, 480, 1); 
        prepare();
    }
    private void prepare()
    {
        //Counter counter = new Counter();
        //addObject(counter, 48, 19);
        Ball ball = new Ball();
        addObject(ball, 320, 240);
        Paddle1 paddle1 = new Paddle1();
        addObject(paddle1, 10, 240);
        Paddle2 paddle2 = new Paddle2();
        addObject(paddle2, 630, 240);
    }
}
Here is the moving Ball class code -- no bouncing your interaction yet:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * This is the Ball for our Pong game.
 * @Robert Griffith 
 * @11/05/2013
 */
public class Ball extends Actor
{
    int x = 320;
    int y = 240;
    int yDirection;
    int ballSpeed;
    int start = 0;

    /**
     * Runs the main program.
     */
    public void act() 
    {
        if (start == 0){ startup(); }
        x = x + ballSpeed;
        y = y + yDirection;
        setLocation(x, y);
    }    
    
        /**
        * Set the direction and speed of the Ball
        */
        public void startup(){  
        start++;
        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { yDirection = -3; } 
        else { yDirection = 3; }

        if ( Greenfoot.getRandomNumber(100) < 50 ) 
        { ballSpeed = -3; } 
        else { ballSpeed = 3; }
    }
}
Next we have the paddles. Here is Paddle1:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * The left paddle (Paddle1) class.
 * @Robert Griffith
 * @11/05/213
 */
public class Paddle1 extends Actor
{
    int y = 240;
    int paddleSpeed = 10;

    /**
     * Run the program.
     */
    public void act() 
    {
        checkKeys();
    }    

    /**
     *  Check keys and move the paddle
     */
    public void checkKeys()
    {
        if ( Greenfoot.isKeyDown("a"))
        {
            y=y-paddleSpeed;
            if (y < 48) {y = 48; } // y = half my paddle height
            setLocation(10, y); // Set the x coordinate at 10
        }
        if ( Greenfoot.isKeyDown("z"))
        {
            y=y+paddleSpeed;
            if (y > 432) {y = 432; } // y = world width - half my paddle height
            setLocation(10, y); // Set the x coordinate at 10
        }
    }
}
Note that Paddle2 is nearly identical to Paddle1 except for being further over on the x axis.

Monday, November 4, 2013

Beginning a Simple Pong Game

We're taking a brief break from our game code to start a project from scratch -- a good thing to do every now and then.

I used Photoshop to create 3 game pieces which I saved as PNG's to preserve the transparency:

  • Left Paddle
  • Right Paddle
  • Ball

I began a new Greenfoot scenario which I called MyPong -- of course you can call yours anything you want. I created a new subclass of World and called it "Board".  \

Next I created three subclasses of Actor and assigned the images (above):

  • Paddle1
  • Paddle2
  • Ball

My Board class looks like this:

import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class Board here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Board extends World
{

    /**
     * Constructor for objects of class Board.
     * 
     */
    public Board()
    {    
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(640, 480, 1); 
        Ball ball = new Ball();
        addObject(ball, 320, 240);
        Paddle1 paddle1 = new Paddle1();
        addObject(paddle1, 10, 240);
        Paddle2 paddle2 = new Paddle2();
        addObject(paddle2, 630, 240);
    }
}
I then "Compiled" my game to verify that everything looked right.

Wednesday, October 30, 2013

Playing with Walls & Fences

Today I would like you to find [or make] some walls and/or fences.  You will create a new Class (i.e. a subclass of Actor) called Wall [or Fence] and set the image.
In this example, you can see fence, plants, roads, walls, etc.  Notice it's not a true top-down, but still works.
Add some walls (or other impassable objects) to your world and then begin the process of setting up the code to not allow actors to pass through them.

For example, in your "move" section, you could do something like this:
  • If you press the "up" key, move up 5
    • If you're now touching the object you can't pass through, move down 5
If you're moving your character 1 block at a time, you can also do:
  • If you can see the wall, don't move -- OR... if you can NOT see the wall, move.
Imagine now that you have other classes -- mud, quicksand, brush, etc.  Maybe if you're in mud, your movespeed is slowed by 1 -- or if you're in quicksand, your move speed is super slow.  Maybe brush is impassable -- but if you hit it with your sword X times, it breaks and goes away.

I'll do my best to demo wall code during class.
In this example, you can see the sprite edges.
Right now we're in a "locked" world with a specific dimension.  But as we learn more code, we'll see how it's possible to have a much larger world with a scrolling exploration mode.  The example below shows a ridiculously large world, but it's definitely a potentially cool game.