후들후들

foodybug.egloos.com

포토로그 마이가든



확률 문제

모 사이트에서 화제가 됐던 문제다.


조커를 뺀 트럼프 카드 52장중에서 카드 1장을 뽑은 뒤,
어떤 카드인지 확인하지 않고 상자에 넣었다.
그리고 남은 카드를 잘 섞은 다음 3장을 뽑았는데, 3장 다 다이아였다.
이 때, 상자 안의 카드가 다이아일 확률은 얼마인가?


정답이 궁금해서 C#으로 돌려보았다.
문제의 핵심은 3장의 다이아 카드를 찾아서 보여주지 않고
3장을 뽑아보고 보니 3장 다 다이아였다는 것인 듯 하다.
간만에 정말 흥미로운 퀴즈였다.




as3.0에 대한 짦은 생각 flex


as3.0은 추상클래스가 지원되지 않는다.(C언어는 순수가상함수를 포함하는 클래스 또는 인터페이스)

자바와 C++ 둘 다 지원되는 기능이 oop를 지향하는 as3.0에는 없다는 게 조금 의외이다.

물론 코드 수준에서 추상클래스를 흉내낼 순 있지만 그건 어디까지나 편법같다.

생성자에서 해당 클래스의 타입을 검사해서 추상클래스로 쓰고자 하는 클래스인 경우에는 런타임에

에러를 내도록 하는 방법이 있긴 하지만 이 방법은 컴파일시에는 아무것도 드러나지 않는다는 것이 문제다.

개인적인 생각에는 플래시의 이러한 약점은 아도비의 나태함을 대변하는 것이 아닐까 싶다.

버젼별로 문법적인 변화도 엄청나고(문법적인 변화로는 부족한 것 같다. 구조적인 변화랄까), 이것은 곧

기존 버젼의 불완전함을 스스로 드러낸 셈이라고 본다. 잡스의 경솔함(?)에는 확실히 이유가 있긴 하다.

자세한 코드는 아래의 블로그를 참조하도록 하자.(유용한 자료가 많은 블로그다. 주인이 as의고수인듯)

http://blog.jidolstar.com/452

as 3.0 프로젝트 Comet v1.0 flex


간단한 피하기 게임을 만들었다.

FSM으로 오브젝트간의 상호작용을 처리해보았다.

as3.0에 맞게끔 포인터나 템플릿을 배제해야해서 생각처럼 간단하지 않았다.

최대한 차후에 수정이 편하게끔 확장성을 고려했고 이 프로젝트를 기반으로

여러 장르로의 게임으로 개발을 해 나갈 계획이다.



광고 사이트 스크롤 플래시 flex




MouseOver_Flash.swf

순수하게 as3.0으로 광고 사이트에 흔히 쓰이는 인터페이스를 구현했다.

이미지 클릭시 링크 주소는 그냥 랜덤.


파티클 부수기(?) 분석 flex


인터넷에 떠돌던 알카노이드 비슷한 게임.

처음에 게임을 시작하면 엄청난 양의 블럭에 당황하게 되지만

블럭이 블럭을 부수면서 엄청나게 늘어나는 공(?) 덕분에 클리어가 가능하다.

화면을 가득채우는 총천연색의 공들이 장관을 이루는, 제작자의 센스가 돋보이는 게임.


package
{
 import flash.display.Bitmap;
 import flash.display.BitmapData;
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.geom.ColorTransform;
 import flash.geom.Matrix;
 import flash.geom.Rectangle;
 import flash.text.TextField;
 import flash.text.TextFieldAutoSize;
 import net.hires.debug.Stats;
 
 [SWF(width = "465", height = "465", frameRate = "30")]
 public class BlockBreaker extends Sprite
 {
  private static const HEIGHT:Number = 465;
  private static const WIDTH:Number = 465;
  private var _canvas:BitmapData;
  private var _blocks:Blocks;
  private var _fallBlocks:Vector.<Particle>;
  private var _balls:Vector.<Particle>;
  private var _bar:Bitmap;
  
  public function BlockBreaker()
  {
   _canvas = new BitmapData(WIDTH, HEIGHT,false,0x000000);
   addChild(new Bitmap(_canvas));
   
   _blocks = new Blocks(WIDTH, 100);
   
   _fallBlocks = new Vector.<Particle>();
   
   var b:BitmapData = new BitmapData(50, 10, false, 0x00FF00);
   addChild(_bar = new Bitmap(b));
   _bar.y = WIDTH -b.width;
   var _ball:Particle = new Particle(WIDTH / 2, HEIGHT / 2);
   _ball.vx = Math.random() *10;
   _ball.vy = -Math.random() *9 -1;
   _ball.color = 0xFFFFFF;
   
   _balls = new Vector.<Particle>();
   _balls.push(_ball);
   
   //var stats:Stats = new Stats();
   //stats.y = 200;
   //addChild(stats);
   
   addEventListener(Event.ENTER_FRAME, update);
  }
  private function update(e:Event):void
  {
   _canvas.lock();
   _canvas.colorTransform(_canvas.rect, new ColorTransform (0.9, 0.5, 0.9));
   
   for each(var block:Particle in _blocks.values)
   {
    if (block)
    {
     _canvas.setPixel(block.x, block.y, block.color);
    }
   }
   var removeBalls:Vector.<Particle> = new Vector.<Particle>();
    for each(var ball:Particle in _balls)
    {
    var bvx:Number = ball.vx;
    var bvy:Number = ball.vy;
    var bspeed:Number = Math.sqrt(bvx * bvx + bvy * bvy);
    var bradius:Number = Math.atan2(bvy, bvx);
    for (var i:int = 0; i < bspeed;i++)
    {
     ball.x += ball.vx/bspeed;
     ball.y += ball.vy/bspeed;
     var hitParticle:Particle = _blocks.getParticle(ball.x, ball.y);
     if(hitParticle)
     {
      var removedP:Particle = _blocks.removeParticle(ball.x, ball.y);
      removedP.vx = Math.cos(bradius+Math.PI*2/(30*Math.random())-15)*3;
      removedP.vy = 1;
      removedP.color = hitParticle.color;
      _fallBlocks.push(removedP);
      ball.vy = -ball.vy;
     }
     
     if ((ball.x < 0 && ball.vx < 0) || (ball.x > WIDTH && ball.vx > 0))
     {
      ball.vx = -ball.vx;
     }
     if (ball.y < 0 && ball.vy < 0)
     {
      ball.vy = -ball.vy;
     }
     if (ball.y > HEIGHT)
     {
      removeBalls.push(ball);
     }
     if (_bar.hitTestPoint(ball.x, ball.y))
     {
      ball.vy = -Math.abs(ball.vy);
     }
     _canvas.setPixel(ball.x, ball.y, ball.color);
    }
   }
   removeBalls.forEach(function(b:Particle, ...args):void {
    var index:int = _balls.indexOf(b);
    if (index != -1)
    {
     _balls.splice(index, 1);
    }
   });
   
   var removeFallBs:Vector.<Particle> = new Vector.<Particle>();
   _fallBlocks.forEach(function(fallP:Particle, ...args):void {
    fallP.vy += 0.1;
    fallP.x += fallP.vx;
    fallP.y += fallP.vy;
    _canvas.setPixel(fallP.x, fallP.y, fallP.color);
    if (_bar.hitTestPoint(fallP.x,fallP.y))
    {
     var newball:Particle = new Particle(fallP.x,fallP.y);
     newball.vx = Math.random() * 10;
     newball.vy = Math.random() * 9 + 1;
     newball.color = fallP.color;
     _balls.push(newball);
     removeFallBs.push(fallP);
    }else if (fallP.y > HEIGHT)
    {
     removeFallBs.push(fallP);
    }
   });
   
   removeFallBs.forEach(function(b:Particle,...args):void{
    var index:int = _fallBlocks.indexOf(b);
    if (index != -1)
    {
     _fallBlocks.splice(index, 1);
    }
   });
   _bar.x = stage.mouseX;
   _canvas.unlock();
   
   if (_blocks.count == 0)
   {
    removeEventListener(Event.ENTER_FRAME, update);
    var clearTF:TextField = new TextField();
    clearTF.text = "CLEAR!\nおめでと";
    clearTF.textColor = 0xFFFFFF;
    clearTF.autoSize = TextFieldAutoSize.LEFT;
    _canvas.draw(clearTF,new Matrix(5,0,0,5,WIDTH/2-clearTF.width*5/2,HEIGHT/2-clearTF.height*5/2));
   }
   
  }
 }
}
import frocessing.color.ColorHSV;
class Blocks
{
 public function get count():int { return _count;}
 private var _count:int;
 public function get width():Number { return _width; }
 private var _width:Number;
 public function get height():Number { return _height; }
 private var _height:Number;
 public var values:Vector.<Particle>;
 function Blocks(width:Number,height:Number)
 {
  _width = width;
  _height = height;
  _count = width * height;
  values = new Vector.<Particle>(width * height, false);
  var c:ColorHSV = new ColorHSV();
  for (var i:int = 0; i < _width; i++)
  {
   c.h = 360 * i / _width;
   for (var j:int = 0 ; j < _height; j++ )
   {
    var p:Particle = new Particle(i, j);
    p.color = c.value;
    values[i + j * _width] = p;
   }
  }
 }
 public function getParticle(x:int, y:int):Particle
 {
  var index:int = x + y * _width;
  if (index >= values.length || index < 0)
  {
   return null;
  }
  return values[x + y * _width];
 }
 public function removeParticle(x:int, y:int):Particle
 {
  var p:Particle = values[x + y * _width];
  if (p)
  {
   _count--;
   values[x + y * _width] = undefined;
  }
  return p;
 }
}
class Particle
{
 public var x:Number;
 public var y:Number;
 public var vx:Number = 0;
 public var vy:Number = 0;
 public var color:uint;
 public function Particle(x:Number=0,y:Number=0 )
 {
  this.x = x;
  this.y = y;
 }
}


  코드를 살펴보면 Blocks라는 구조체는 처음 시작할때의 블럭들의 정보만 담는듯 보인다. 개개의 블럭은

Particle이라는 구조체로 구성되는데 위에 보이듯 별 특별한 역할은 없다. blocks의 particle이 파괴되면

해당 particle을 blocks에서 제거하고 _fallBlocks라는 벡터에 해당 파티클의 정보를 속도를 조절해서

추가하는 듯 하다.

  BlockBreaker라는 클래스가 핵심 클래스인데 생성자에서 여러 정보를 초기화해준다. 생성자에서 addChild

되는건 배경맵과 _bar(플레이어가 조종하는 공 튕기는 막대)뿐이다. _ball은 현재 플레이어가 튕길 수 있는

공의 벡터이다. 생성자의 마지막 부분에는 update 함수를 addEventListener에 추가해서 게임을 실행한다.

update 함수의 초반의 for each 문에서는 _blocks의 파티클 수 만큼 반복문을 돌면서 해당 픽셀의 위치와 컬러에 따라

_canvas(배경맵)의 픽셀을 바꿔준다. 굳이 addChild하지 않아도 되게끔 구성한 듯 하다.

그 밑의 반복문은 _ball의 파티클 수만큼 각 볼의 위치를 속도만큼 변경하고 튕기는 판정을 한다. Blocks과 충돌 판정을

매 프레임 거쳐 _fallBlocks에 파티클을 추가하고 벽과 막대에 대한 충돌 판정도 행하는데 이 반복문은 bspeed라는

값에 의해 내부적으로 다시 한번 루프를 돈다. bspeed는 볼의 x,y 속도값의 대각선 성분(x,y 각각의 제곱을 더한 값의

제곱근)의 크기인데 이것은 update함수에 따른 ball의 위치가 불연속적이라는 문제를 해결하기 위한 부분이다.

bspeed의 크기만큼 속도를 나눠 반복문 내에서 반복해주면 ball이 update 함수의 한번의 루프동안 이동한 모든 경로에

대해 충돌 판정이 가능해진다(게임중에 간혹 공이 위쪽 블럭 내부로 들어가서 블럭들을 헤집어 놓고 텅 빈 공간을 만드는

경우가 발생하는데 이 로직 때문으로 보인다 ). bspeed와 관련된 루프에선 원래 속도에서 bspeed만큼을 나눠준 값을

루프 내의 속도로 취해 ball의 위치를 계산한다. 화면 바깥으로 나가는 공은 removeBalls에 추가되는데 바로 밑의 함수가

removeBalls에 추가된 공만큼 _balls의 공을 제외시킨다. 아래의 _fallBlocks를 처리하는 부분도 위와 흡사하게

구성돼있다. 가장 하단에는 _blocks의 count를 헤아려 모든 블럭이 파괴되면 게임을 종료한다.

  update 함수의 가장 위쪽의 lock()은 최적화를 위한 코드로 unlock() 이후에 setpixel의 변화를 bitmap 클래스가

인지하도록 하는 함수이다. 20% 정도 속도를 향상시킬 수 있다고 한다.

  매 프레임마다 colorTransform()을 실행하게 되는데, 이것은 지정한 rect 구역을 인자값만큼 색상변환을 해준다.

픽셀들이 궤적을 남기며 이동하는 부분을 이 함수 하나로 간단하게 표현해냈다.

BlockBreaker.swf




1 2 3