for ActionScript3
PongPod - Source code
(C) Kenta Cho(ABA."Saba")
PongPod.as
/**
* PongPod.<br>
* Play a pong with a wheel.<br><br>
*
* Copyright (C) 2005 Kenta Cho. Some rights reserved.
*
* - 画面下の灰色の丸(ホイール)にカーソルを合わせてください
* - クリックするとゲームが始まります
* - ホイールに沿って丸くカーソルを動かすとラケットが動きます
* - ラケットでボールをはじき返してください
* - ボールが中央の黒丸に入るとミスです
* - ミス3つでゲームオーバー
*/
package {
import flash.display.Sprite;
import flash.display.TextField;
import flash.display.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.EventType;
import flash.events.MouseEventType;
// Use '-default-size 340 550 -default-frame-rate 30'
// as the additional compiler arguments.
const WIDTH:int = 340;
const HEIGHT:int = 550;
const FRAMERATE:int = 30;
const PI:Number = 3.1415;
const HALF_PI:Number = PI / 2;
const TWO_PI:Number = PI * 2;
var field:Field;
var wheel:Wheel;
var racket:Racket;
var ball:Array;
const BALL_NUM:int = 25;
const TITLE:int = 0;
const IN_GAME:int = 1;
const GAMEOVER:int = 2;
var state:int;
var title:Title;
var gameover:Gameover;
var score:int;
var se:Se;
var mousePressed:Boolean;
public class PongPod extends Sprite {
public function PongPod() {
graphics.beginFill(0xf8f8f8);
graphics.drawRect(0, 0, WIDTH, HEIGHT);
graphics.endFill();
field = new Field;
field.init(this);
addChild(field);
wheel = new Wheel;
wheel.init();
addChild(wheel);
racket = new Racket;
racket.init();
addChild(racket);
title = new Title;
title.init(this);
addChild(title.titleText);
gameover = new Gameover;
gameover.init(this);
addChild(gameover.gameoverText);
se = new Se;
se.load();
Util.init();
addChild(Util.scoreText);
addEventListener(EventType.ENTER_FRAME, onEnterFrame);
mousePressed = false;
addEventListener(MouseEventType.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEventType.MOUSE_UP, onMouseUp);
startTitle();
}
public function startTitle() {
state = TITLE;
Util.scoreText.visible = false;
racket.visible = false;
title.start();
}
public function startInGame() {
state = IN_GAME;
score = 0;
Util.updateScore();
Util.scoreText.visible = true;
racket.visible = true;
se.init();
BallPool.initBalls();
field.start();
racket.start();
}
public function startGameover() {
state = GAMEOVER;
racket.visible = false;
BallPool.initBalls();
gameover.start();
}
public function onEnterFrame(evt:Event) {
switch (state) {
case TITLE:
field.draw();
wheel.move();
title.draw();
break;
case IN_GAME:
field.draw();
field.move();
wheel.move();
BallPool.moveBalls();
racket.move();
racket.draw();
BallPool.drawBalls();
se.playAllSes();
break;
case GAMEOVER:
field.draw();
wheel.move();
gameover.draw();
break;
}
}
public function onMouseDown(evt:Event) {
mousePressed = true;
}
public function onMouseUp(evt:Event) {
mousePressed = false;
}
public function getMousePressed():Boolean {
return mousePressed;
}
}
private class Util {
public static var scoreText:TextField;
public static function init() {
scoreText = new TextField;
scoreText.x = 230;
scoreText.y = 10;
scoreText.autoSize = TextFieldAutoSize.RIGHT;
var format:TextFormat = new TextFormat;
format.font = "Verdana";
format.color = 0xffffff;
format.size = 20;
scoreText.defaultTextFormat = format;
}
public static function updateScore() {
scoreText.text = String(score);
}
public static function adjustDeg(d:Number):Number {
if (d > PI)
return d - PI * 2;
else if (d < -PI)
return d + PI * 2;
else
return d;
}
public static function dist
(x1:Number, y1:Number, x2:Number, y2:Number):Number {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
}
/**
* 2D vector.
*/
private class Vector {
public var x:Number;
public var y:Number;
}
/**
* My racket.
*/
private class Racket extends Sprite {
const SENSITIVITY:Number = 0.7;
var radius:Number;
var deg:Number;
var pos:Array;
var p1:Vector;
var p2:Vector;
var rWidth:Number;
var rHeight:Number;
public function init() {
radius = field.bwRadius * 1.5;
pos = new Array;
for (var i:int = 0; i < 2; i++)
pos[i] = new Vector;
p1 = new Vector;
p2 = new Vector;
rWidth = 70;
rHeight = 7;
x = field.pos.x + field.centerPos.x;
y = field.pos.y + field.centerPos.y;
graphics.beginFill(0x000000);
graphics.drawRect(-rWidth / 2, -radius - rHeight / 2, rWidth, rHeight);
graphics.drawRect(-rWidth / 2, radius - rHeight / 2, rWidth, rHeight);
graphics.endFill();
}
public function start() {
deg = 0;
}
public function move() {
deg -= wheel.angVel * SENSITIVITY;
deg = Util.adjustDeg(deg);
var d:Number = deg;
for (var i:int = 0; i < pos.length; i++) {
pos[i].x = field.centerPos.x - Math.sin(d) * radius;
pos[i].y = field.centerPos.y + Math.cos(d) * radius;
p1.x = pos[i].x - Math.sin(d + HALF_PI) * rWidth / 2;
p1.y = pos[i].y + Math.cos(d + HALF_PI) * rWidth / 2;
p2.x = pos[i].x - Math.sin(d - HALF_PI) * rWidth / 2;
p2.y = pos[i].y + Math.cos(d - HALF_PI) * rWidth / 2;
for (var j:int = 0; j < ball.length; j++)
if (ball[j].isExist)
if (ball[j].checkHit(p1, p2))
ball[j].reflectWithRacket(d + HALF_PI);
d += PI;
}
}
public function draw() {
rotation = deg * 180 / PI;
}
}
/**
* Balls.
*/
private class Ball extends Sprite {
var pos:Vector;
var ppos:Vector;
var deg:Number;
var speed:Number;
var size:Number;
var trgSize:Number;
var isExist:Boolean;
var reflectCnt:int;
var idx:int;
public function Ball() {
pos = new Vector;
ppos = new Vector;
graphics.beginFill(0xffffff);
graphics.drawCircle(0, 0, 1);
graphics.endFill();
}
public function set(x:Number, y:Number, d:Number, sp:Number, sz:Number) {
pos.x = x;
pos.y = y;
deg = Util.adjustDeg(d);
speed = sp;
trgSize = sz;
size = 0.1;
reflectCnt = 0;
draw();
isExist = true;
visible = true;
}
public function move() {
ppos.x = pos.x;
ppos.y = pos.y;
pos.x -= Math.sin(deg) * speed;
pos.y += Math.cos(deg) * speed;
size += (trgSize - size) * 0.1;
if (pos.x < 0 || pos.x >= field.size.x)
reflect(0);
if (pos.y < 0 || pos.y >= field.size.y)
reflect(HALF_PI);
if (reflectCnt > 0)
reflectCnt--;
if (Util.dist(pos.x, pos.y, field.centerPos.x, field.centerPos.y)
< field.bwRadius * 0.75 + size) {
field.incOutCnt();
isExist = false;
visible = false;
return;
}
}
private function reflect(d:Number) {
var aod1:Number = Math.abs(Util.adjustDeg(deg - d));
var aod2:Number = Math.abs(Util.adjustDeg(deg - (d + PI)));
var od:Number;
var aod:Number;
if (aod1 < aod2) {
od = Util.adjustDeg(deg - d);
aod = aod1;
} else {
od = Util.adjustDeg(deg - (d + PI));
aod = aod2;
}
if (od < 0)
deg += aod * 2;
else
deg -= aod * 2;
pos.x = ppos.x;
pos.y = ppos.y;
pos.x -= Math.sin(deg) * speed;
pos.y += Math.cos(deg) * speed;
se.playSe(idx % 7);
}
public function reflectWithRacket(d:Number) {
if (reflectCnt <= 0) {
reflect(d);
se.playSe(7);
score++;
Util.updateScore();
}
reflectCnt = 3;
}
public function checkHit(p:Vector, pp:Vector):Boolean {
var bmvx:Number;
var bmvy:Number;
var inaa:Number;
bmvx = pp.x;
bmvy = pp.y;
bmvx -= p.x;
bmvy -= p.y;
inaa = bmvx * bmvx + bmvy * bmvy;
if (inaa > 0.00001) {
var sofsx:Number;
var sofsy:Number;
var inab:Number;
var hd:Number;
sofsx = pos.x;
sofsy = pos.y;
sofsx -= p.x;
sofsy -= p.y;
inab = bmvx * sofsx + bmvy * sofsy;
if (inab >= 0 && inab <= inaa) {
hd = sofsx * sofsx + sofsy * sofsy - inab * inab / inaa;
if (hd >= 0 && hd <= size * 0.7 + 7.0) {
return true;
}
}
}
return false;
}
public function draw() {
x = pos.x + field.pos.x;
y = pos.y + field.pos.y;
scaleX = scaleY = size;
}
}
private class BallPool {
static var ballIdx:int = 0;
public static function initBalls() {
for (var i:int = 0; i < ball.length; i++) {
ball[i].idx = i;
ball[i].isExist = false;
ball[i].visible = false;
}
ballIdx = 0;
}
public static function moveBalls() {
for (var i:int = 0; i < ball.length; i++)
if (ball[i].isExist)
ball[i].move();
}
public static function drawBalls() {
for (var i:int = 0; i < ball.length; i++)
if (ball[i].isExist)
ball[i].draw();
}
public static function getBallInstance():Ball {
for (var i:int = 0; i < ball.length; i++) {
if (!ball[ballIdx].isExist)
return ball[ballIdx];
ballIdx++;
if (ballIdx >= ball.length)
ballIdx = 0;
}
return null;
}
}
/**
* Game field (main screen).
*/
private class Field extends Sprite {
var pongPod:PongPod;
var blackhole:Blackhole;
var outCountBall:Array;
var pos:Vector;
var size:Vector;
var bwRadius:Number;
var centerPos:Vector;
var bwSize:Number;
var cnt:int;
var outCnt:int;
public function init(pongPod:PongPod) {
this.pongPod = pongPod;
size = new Vector;
size.x = 320;
size.y = 240;
pos = new Vector;
pos.x = pos.y = (WIDTH - size.x) / 2;
bwRadius = size.y * 0.1;
centerPos = new Vector;
centerPos.x = size.x / 2;
centerPos.y = size.y / 2;
changeBrightness(0);
bwSize = 0;
ball = new Array;
for (var i:int = 0; i < BALL_NUM; i++) {
ball[i] = new Ball;
addChild(ball[i]);
}
blackhole = new Blackhole;
blackhole.x = pos.x + centerPos.x;
blackhole.y = pos.y + centerPos.y;
draw();
addChild(blackhole);
outCountBall = new Array;
for (var i:int = 0; i < 3; i++) {
outCountBall[i] = new OutCountBall;
outCountBall[i].x = size.x / 2 - 10 + i * 10 + pos.x;
outCountBall[i].y = size.y / 2 + pos.y;
addChild(outCountBall[i]);
}
clearOutCountBall();
}
public function start() {
bwSize = 1;
cnt = 0;
outCnt = 0;
for (var i:int = 0; i < 3; i++)
addBall();
clearOutCountBall();
}
private function clearOutCountBall() {
for (var i:int = 0; i < outCountBall.length; i++)
outCountBall[i].visible = false;
}
public function addBall() {
var b:Ball = BallPool.getBallInstance();
if (b == null)
return;
var x:Number;
var y:Number;
if (Math.random() >= 0.5) {
x = Math.random() * (field.size.x - 40) + 20;
if (Math.random() >= 0.5)
y = 20;
else
y = field.size.y - 20;
} else {
y = Math.random() * (field.size.y - 40) + 20;
if (Math.random() >= 0.5)
x = 20;
else
x = field.size.x - 20;
}
b.set(x, y, Math.random() * TWO_PI,
1.5 + Math.random() * 1.0, 4 + Math.random() * 3.0);
}
public function incOutCnt() {
se.playSe(8);
outCountBall[outCnt].visible = true;
outCnt++;
if (outCnt >= 3) {
pongPod.startGameover();
clearOutCountBall();
}
}
public function move() {
cnt++;
if (cnt % 300 == 0)
addBall();
}
public function draw() {
blackhole.scaleX = blackhole.scaleY = bwSize;
}
public function changeBrightness(r:Number) {
graphics.beginFill(Math.floor(80 * r) * 0x10000 +
Math.floor(200 * r) * 0x100 +
Math.floor(80 * r));
graphics.drawRect(pos.x, pos.y, size.x, size.y);
graphics.endFill();
}
}
private class Blackhole extends Sprite {
public function Blackhole() {
graphics.beginFill(0x000000);
graphics.drawCircle(0, 0, field.bwRadius);
graphics.endFill();
}
}
private class OutCountBall extends Sprite {
public function OutCountBall() {
graphics.beginFill(0xffffff);
graphics.drawCircle(0, 0, 5);
graphics.endFill();
}
}
/**
* Wheel that controls a racket.
*/
private class Wheel extends Sprite {
var centerPos:Vector;
var radFrom:Number;
var radTo:Number;
var cursorOn:Boolean;
var angVel:Number;
var pdeg:Number;
public function init() {
centerPos = new Vector;
centerPos.x = WIDTH / 2;
centerPos.y = HEIGHT * 0.75;
radFrom = WIDTH * 0.35 / 2;
radTo = WIDTH * 0.7 / 2;
cursorOn = false;
angVel = 0;
pdeg = 0;
x = centerPos.x;
y = centerPos.y;
graphics.beginFill(210 * 0x10000 + 210 * 0x100 + 210);
graphics.drawCircle(0, 0, radTo);
graphics.endFill();
graphics.beginFill(0xffffff);
graphics.drawCircle(0, 0, radFrom);
graphics.endFill();
}
public function move() {
var d:Number = Util.dist(mouseX, mouseY, 0, 0);
if (d >= radFrom * 0.05 && d <= radTo * 1.4) {
if (!cursorOn) {
cursorOn = true;
//cursor(HAND);
pdeg = Math.atan2(mouseX, mouseY);
}
} else {
if (cursorOn) {
cursorOn = false;
//cursor(ARROW);
}
}
if (cursorOn) {
var deg:Number = Math.atan2(mouseX, mouseY);
angVel = Util.adjustDeg(deg - pdeg);
pdeg = deg;
} else {
angVel = 0;
}
}
}
/**
* Title screen.
*/
private class Title {
var pongPod:PongPod;
var cnt:int;
var titleText:TextField;
public function init(pongPod:PongPod) {
this.pongPod = pongPod;
titleText = new TextField;
var format:TextFormat = new TextFormat;
format.font = "Verdana";
format.color = 0xffffff;
format.size = 35;
titleText.defaultTextFormat = format;
titleText.text = "PongPod";
titleText.autoSize = TextFieldAutoSize.CENTER;
titleText.x = field.pos.x + field.size.x / 2 - titleText.width / 2;
titleText.y = field.pos.y + field.size.y / 2 - titleText.height / 2;
titleText.visible = false;
}
public function start() {
cnt = 0;
field.changeBrightness(0);
field.bwSize = 0;
}
public function draw() {
cnt++;
if (cnt > 30 && pongPod.getMousePressed() && wheel.cursorOn) {
titleText.visible = false;
pongPod.startInGame();
return;
}
if (cnt <= 30)
field.changeBrightness(cnt * (1.0 / 30));
if (cnt == 45)
titleText.visible = true;
}
}
/**
* Gameover screen.
*/
private class Gameover {
var pongPod:PongPod;
var cnt:int;
var gameoverText:TextField;
public function init(pongPod:PongPod) {
this.pongPod = pongPod;
gameoverText = new TextField;
var format:TextFormat = new TextFormat;
format.font = "Verdana";
format.color = 0xffffff;
format.size = 27;
gameoverText.defaultTextFormat = format;
gameoverText.text = "GameOver";
gameoverText.autoSize = TextFieldAutoSize.CENTER;
gameoverText.x = field.pos.x + field.size.x / 2 - gameoverText.width / 2;
gameoverText.y = field.pos.y + field.size.y / 2 - gameoverText.height / 2;
gameoverText.visible = false;
}
public function start() {
cnt = 0;
}
public function draw() {
cnt++;
if (cnt > 300 || (cnt > 30 && pongPod.getMousePressed() && wheel.cursorOn)) {
gameoverText.visible = false;
pongPod.startTitle();
return;
}
if (cnt <= 30) {
field.bwSize = 1.0 + cnt * 0.1;
field.changeBrightness(1.0 - cnt * (1.0 / 30));
}
if (cnt == 45)
gameoverText.visible = true;
}
}
/**
* Sound effect.
*/
private class Se {
const SE_NUM:int = 9;
var se:Array;
var play:Array;
var cnt:int;
var interval:int = 7;
public function load() {
se = new Array;
play = new Array;
for (var i:int = 0; i < SE_NUM; i++) {
se[i] = new Sound(new URLRequest("se0" + (i + 1) + ".mp3"));
play[i] = false;
}
}
public function init() {
for (var i:int = 0; i < SE_NUM; i++)
play[i] = false;
cnt = 0;
}
public function playSe(n:int) {
play[n] = true;
}
public function playAllSes() {
cnt++;
var ci:int = cnt % interval;
if (ci == 0) {
for (var i:int = 0; i < SE_NUM; i++) {
if (play[i]) {
se[i].play();
play[i] = false;
}
}
}
}
}
}