SlideShare uma empresa Scribd logo
1 de 98
Class 14
iOS 應⽤用軟體設計
課程⼤大綱
•   ⽤用 Processing 學習繪圖程式邏輯
    •   QV081:球移動反彈
    •   QV082:Objective-C 的物件思維

•   Objective-C 類別與⾃自訂類別
    •   QV080:⾃自訂類別

    •   QV084:⾃自訂類別,移動碰撞
    •   QV085:兩個物件互相碰撞

    •   QV086:三個物件 (動作定義在物件上)
    •   QV087:⽤用陣列處理更多的球

•   Quartz 2D
    •   QV136:Quartz 2D
    •   QV137:動畫精靈 (Sprite)

    •   Samples:QuartzFun, GLFun, QuartzDemo
Processing
Processing 語⾔言特性


• 適合學習基礎的程式邏輯
• 畫⾯面上的繪圖呈現
程式結構
•   void setup()
    {
      // 設定的部份
    }


•   void draw()
    {
      // 時間迴圈的部份
    }
資料型態

• int
• float
• 陣列 int[], float[]
起始設定畫布

• size( w, h)
• smooth( )
• frameRate( n )
• background( R, G, B )
常⽤用的畫圖指令
• point(x, y)
• line(x1, y1, x2, y2)
• rect(x1, y1, x2, y2)
• ellipse(x1, y1, w, h)
• triangle(x1, y1, x2, y2, x3, y3)
• quad(x1, y1, x2, y2, x3, y3, x4, y4)
• bezier(x1, y1, x2, y2, x3, y3, x4, y4)
顏⾊色指令

• fill( R, G, B )
• noFill()
• stroke( R, G, B)
• strokeWeight( w )
• noStroke()
更多 Processing 的程式技巧
範例:

 移動球,撞牆反彈
Project QV081

 球移動,碰到周圍反彈
   計時器 NSTimer
   UI 物件移動位置
 常數定義與變數使⽤用
 .....傳統 C 語⾔言的思維
ViewController.xib
ViewController.h

#import <UIKit/UIKit.h>

extern const float areaWidth;
extern const float areaHeight;
                                           常數宣告

@interface ViewController : UIViewController
{
    IBOutlet UIImageView *ball;
    float ballMovementX, ballMovementY; // 每次移動的量
}

- (void) initializeTimer;
- (void) gameLoop: (NSTimer *) theTimer;     變數宣告
@end
常數宣告            ViewController.m (1/2)
const float areaWidth = 320;
const float areaHeight = 460;

@implementation ViewController

- (void) initializeTimer
{
    float theInterval = 1.0 / 30.0;
    [NSTimer scheduledTimerWithTimeInterval:theInterval target:self
             selector:@selector(gameLoop:) userInfo:nil repeats:YES];
}

- (void) gameLoop: (NSTimer *) theTimer
{
    ****** 省略部份程式 ******
}

- (void)viewDidLoad                                 計時器使⽤用
{
    [super viewDidLoad];
                                                  (基本遊戲迴圈)
    // 起始設定
    ballMovementX = 4.0;                        速度控制之參數
    ballMovementY = 4.0;
                                                (1) 遞增量
    [self initializeTimer];
}                                               (2) 時間間隔
ViewController.m (2/2)

- (void) gameLoop: (NSTimer *) theTimer
{
    // 取得新位置
     float ballX = ball.center.x;
     float ballY = ball.center.y;

     ballX += ballMovementX;
     ballY += ballMovementY;

     ball.center = CGPointMake(ballX, ballY);         移⾄至新位置
    // 檢查碰撞到周圍
    if(ballX > areaWidth || ballX < 0)
    {
        ballMovementX = -ballMovementX;
    }
    if(ballY > areaHeight || ballY < 0)
    {
        ballMovementY = -ballMovementY;
    }
}
思考訓練
 考慮有哪些參數是適合單獨定義,以
 便讓程式的彈性增加
  框框⼤大⼩小,位置
  遞增量
  預設位置、預設⽅方向
  ......
讓程式更具彈性

• 定義的資料儘可能獨⽴立,且保持彈性
 • 畫⾯面⼤大⼩小 (320x460)
  ===> 改為常數或變數定義

• 物件⼤大⼩小 (40x40⾃自訂)
  ===> 抓取物件的實際值
Objective-C 希望......


• 能⽤用類別物件時,儘可能⽤用類別
 (避開直覺的資料型態)

• 不重覆變數,減少記憶體的使⽤用
Project QV082

與 Project QV081 ⽐比較...
球移動,碰到周圍反彈
Objective-C 物件的使⽤用
.....Objective-C 的思維
ViewController.h


#import <UIKit/UIKit.h>

extern const CGRect CGRectArea;            "物件" 常數宣告
@interface ViewController : UIViewController
{
    IBOutlet UIImageView *ball;
    CGPoint ballMovement;    // 每次移動的量
}
@property(nonatomic,retain) IBOutlet UIImageView *ball;

- (void) initializeTimer;
- (void) gameLoop: (NSTimer *) theTimer;           物件內含
@end                                               較多資訊
                                                     (x, y)
#import "ViewController.h"                      ViewController.m (1/2)

@implementation ViewController
@synthesize ball;

const CGRect CGRectArea = { {10.0f, 10.0f}, {300.0f, 440.0f} };

- (void) initializeTimer
{                                                         常數宣告
    float theInterval = 1.0 / 30.0;
    [NSTimer scheduledTimerWithTimeInterval:theInterval target:self
             selector:@selector(gameLoop:) userInfo:nil repeats:YES];
}

- (void) gameLoop: (NSTimer *) theTimer
{
    ****** 省略部份程式 ******
}

- (void)viewDidLoad
{
    [super viewDidLoad];                           物件變數使⽤用
    // 起始設定
    ballMovement = CGPointMake(4.0, 4.0);
    [self initializeTimer];
}
ViewController.m (2/2)


                                                               移⾄至計算後
- (void) gameLoop: (NSTimer *) theTimer
{                                                               的新位置
    // 取得新位置
    ball.center = CGPointMake(ball.center.x+ballMovement.x,
                              ball.center.y+ballMovement.y);

    // 檢查碰撞到周圍

    if(ball.center.x > (CGRectGetMinX(CGRectArea)-ball.frame.size.width/2) ||
       ball.center.x < (CGRectGetMinX(CGRectArea)+ball.frame.size.width/2))
    {
        ballMovement.x *= -1;;
    }

    if(ball.center.y > (CGRectGetMaxY(CGRectArea)-ball.frame.size.height/2) ||
       ball.center.y < (CGRectGetMinY(CGRectArea)+ball.frame.size.height/2))
    {
        ballMovement.y *= -1;
    }
}
挑戰練習



 有多個球在畫⾯面亂動時該怎麼處理?
範例:

⾃自訂類別
Project QV080



開發者⾃自建 UIView 類別
選擇這裡新增檔案 (類別)
選擇 Objective-C class
(1) 輸⼊入類別名稱




(2) 選擇『繼承』⾃自 UIView類別
#import <UIKit/UIKit.h>            MYView.h
@interface MYView : UIView

@end
                                                    MYView.m
#import "MYView.h"
@implementation MYView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code                     開新檔時預設的內容
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during ......
- (void)drawRect:(CGRect)rect
{
     // Drawing code
}
*/
@end
MYView.h




#import <UIKit/UIKit.h>

@interface MYView : UIView           property
{
    UIImage *img;
}

- (id) initImage:(CGRect)frame filename:(NSString *)name;

@end



                          method
              (此例有改寫起始⽅方法,
                    故需要宣告)
MYView.m

#import "MYView.h"

@implementation MYView

- (id) initImage:(CGRect)frame filename:(NSString *)name
{
    if(self = [super initWithFrame:frame])
    {
        img = [UIImage imageNamed:name];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
                                                     ⾃自⼰己的程式
{
    CGPoint p;
    p.x = 0;
    p.y = 0;
    [img drawAtPoint:p];
}

@end
ViewController.h
在主程式中使⽤用⾃自訂類別



  #import <UIKit/UIKit.h>

  #import "MYView.h"              引⼊入類別

  @interface ViewController : UIViewController
  {
      MYView *imageView;
  }

  @end


               使⽤用⾃自訂類別,產⽣生物件
ViewController.m

#import "ViewController.h"

@implementation ViewController
                                        此例中同時⽰示範不藉由 xib 產⽣生畫⾯面的⽅方式
- (void) loadView
{
                                       注意 loadView 及 viewDidLoad ⽤用法時機
    // 設置主要視圖
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen]
                                                     applicationFrame]];
    // 將背景設為⽩白⾊色
    self.view.backgroundColor = [UIColor whiteColor];

    // 產⽣生 MYView 並初始化
    imageView = [[MYView alloc] initImage:CGRectMake(100.0f, 200.0f, 48.0f, 48.0f)
                                 filename:@"bomb.png"];
    // 背景設為透明
    imageView.backgroundColor = [UIColor clearColor];

    // 將 MYView 新增於主視圖
    [self.view addSubview:imageView];
}


- (void)viewDidLoad
{
                                              使⽤用⾃自訂類別
    [super viewDidLoad];
! // Do any additional setup after loading the view, typically from a nib.
}
⾃自我練習

 在主程式增加功能,讓此類別物件產
 ⽣生動作 (例如:讓它移動)
 在類別內增加功能
 (property, method, ......)
程式參考                         範例:讓此炸彈掉落


- (void) loadView
{
    ****** 省略部份程式 ******

    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self
             selector:@selector(gameLoop:) userInfo:nil repeats:YES];
}

- (void) gameLoop: (NSTimer *)theTimer
{
    // 取得物件,移⾄至新位置 (往下掉)
    imageView.center = CGPointMake(imageView.center.x, imageView.center.y+1);

    if(imageView.center.y>=self.view.frame.size.height)
    {
        imageView.center = CGPointMake(imageView.center.x, 0);
    }

}


                   此處 imageView 為⾃自訂類別的物件
範例:

⾃自定類別,移動碰撞
Project QV084

⾃自訂類別的物件
在畫⾯面上移動,撞牆反彈
學習重點:⾃自訂物件的移動
(遊戲設計中稱為 sprite
『動畫精靈』)
⾃自定類別
MYView.h
       ⾃自定類別的宣告



#import <UIKit/UIKit.h>

@interface MYView : UIView
{
    UIImage *img;
}

- (id) initImage:(CGRect)frame filename:(NSString *)name;

@end
⾃自定類別的實作                                      MYView.m


#import "MYView.h"

@implementation MYView

- (id) initImage:(CGRect)frame filename:(NSString *)name
{
    if(self = [super initWithFrame:frame])
    {
        img = [UIImage imageNamed:name];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    CGPoint p;
     p.x = 0;
     p.y = 0;
     [img drawAtPoint:p];
}
@end
ViewController.h




#import <UIKit/UIKit.h>
#import "MYView.h"

@interface ViewController : UIViewController
{
    MYView *imageView;

    float incX, incY;
}                                       要移動的量
@end                                   直接⽤用浮點數
ViewController.m (1/2)


- (void)viewDidLoad
{
    [super viewDidLoad];

    incX = 2.0;                         要移動的量
    incY = 1.0;

    // 產⽣生 MYView 並初始化
    imageView = [[MYView alloc]
                  initImage:CGRectMake(100.0f, 200.0f, 64.0f, 64.0f)
                   filename:@"3d_ball.png"];

    // 背景設為透明
    imageView.backgroundColor = [UIColor clearColor];

    // 將 MYView 新增於主視圖
    [self.view addSubview:imageView];


    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self
             selector:@selector(gameLoop:) userInfo:nil repeats:YES];
}
檢查碰撞,
                                                 ViewController.m (2/2)
            若到邊緣則反彈

- (void) gameLoop: (NSTimer *)theTimer
{
    // 取得物件,移⾄至新位置
    imageView.center = CGPointMake(imageView.center.x+incX,
                                   imageView.center.y+incY);

    // 檢查碰撞到周圍
    if(imageView.center.x >=
          (self.view.frame.size.width-imageView.frame.size.width/2) ||
       imageView.center.x <=
          (0+imageView.frame.size.width/2))
    {
        incX *= -1;
    }

    if(imageView.center.y >=
          (self.view.frame.size.height-imageView.frame.size.height/2) ||
       imageView.center.y <=
          (0+imageView.frame.size.height/2))
    {
        incY *= -1;
    }
}
挑戰練習



 使⽤用多個物件......
範例:

兩個物件,互相碰撞
Project QV085
延續 Project QV084
⾃自訂類別,產⽣生兩個物件
在畫⾯面上移動,撞牆反彈
球與球相撞時亦反彈
學習重點:尚未使⽤用到物件
的優點 (不是很理想的寫法)
⾃自定類別
(與之前相同)
ViewController.h
       ⾃自定類別的宣告



#import <UIKit/UIKit.h>
#import "MYView.h"

@interface ViewController : UIViewController
{
    MYView *imageView1;
    float incX1, incY1;

    MYView *imageView2;                 宣告兩個物件
    float incX2, incY2;
}                                  以及各別物件的移動量
@end                                (不是很聰明的⽤用法)
- (void)viewDidLoad
{
                                                       ViewController.m (1/3)
    [super viewDidLoad];

    // ********** 第⼀一個球 **********
    incX1 = 2.0;
    incY1 = 1.5;

    // 產⽣生 MYView 並初始化
    imageView1 = [[MYView alloc]
          initImage:CGRectMake(100.0f, 200.0f, 48.0f, 48.0f)
           filename:@"bb_blue.png"];
    // 背景設為透明
    imageView1.backgroundColor = [UIColor clearColor];
    // 將 MYView 新增於主視圖
    [self.view addSubview:imageView1];
                                                                重覆撰寫
    // ********** 第⼆二個球 **********
                                                               各別的運動
    incX2 = 1.0;
    incY2 = 2.5;

    // 產⽣生 MYView 並初始化
    imageView2 = [[MYView alloc]
          initImage:CGRectMake(80.0f, 120.0f, 48.0f, 48.0f)
           filename:@"bb_red.png"];
    // 背景設為透明
    imageView2.backgroundColor = [UIColor clearColor];
    // 將 MYView 新增於主視圖
    [self.view addSubview:imageView2];

    ****** 省略部份程式 ******
}
ViewController.m (2/3)
- (void) gameLoop: (NSTimer *)theTimer
{
    // ********** 第⼀一個球 **********
    // 取得物件,移⾄至新位置
    imageView1.center = CGPointMake(imageView1.center.x+incX1, imageView1.center.y+incY1);
    // 檢查碰撞到周圍
    if(imageView1.center.x>=(self.view.frame.size.width-imageView1.frame.size.width/2) ||
       imageView1.center.x<=(0+imageView1.frame.size.width/2))
    {
        incX1 *= -1;
    }
    if(imageView1.center.y>=(self.view.frame.size.height-imageView1.frame.size.height/2) ||
       imageView1.center.y<=(0+imageView1.frame.size.height/2))
    {
        incY1 *= -1;
    }

    // ********** 第⼀一個球 **********
    // 取得物件,移⾄至新位置
    imageView2.center = CGPointMake(imageView2.center.x+incX2, imageView2.center.y+incY2);
    // 檢查碰撞到周圍
    if(imageView2.center.x>=(self.view.frame.size.width-imageView2.frame.size.width/2) ||
       imageView2.center.x<=(0+imageView2.frame.size.width/2))
    {
        incX2 *= -1;
    }
    if(imageView2.center.y>=(self.view.frame.size.height-imageView2.frame.size.height/2) ||
       imageView2.center.y<=(0+imageView2.frame.size.height/2))
    {
        incY2 *= -1;
    }

    ****** 省略部份程式 ******
}
- (void) gameLoop: (NSTimer *)theTimer             ViewController.m (1/3)
{
    ****** 省略部份程式 ******

    // 處理碰撞
    float dist = sqrtf(powf(imageView1.center.x-imageView2.center.x, 2)
                      +powf(imageView1.center.y-imageView2.center.y, 2));
    if(dist<=(imageView1.frame.size.width/2+imageView2.frame.size.width/2))
    {
        // 不是很漂亮的⽅方法
        /*
        incX1 *= -1;
        incY1 *= -1;                                 檢查是否碰撞
        incX2 *= -1;         直接反向
        incY2 *= -1;                             (兩球圓⼼心的距離是否
        */
                                                  ⼩小於兩圓半徑之和)
        // 能量守恆的⽅方式
        float incX1Temp = incX1;
        float incX2Temp = incX2;
        float incY1Temp = incY1;
        float incY2Temp = incY2;
        incX1 = incX2Temp;                沒什麼了不起的
        incY1 = incY2Temp;
                                             物理原理
        incX2 = incX1Temp;
        incY2 = incY1Temp;
    }
}
程式思考


 出現更多個球 (希望不會寫到瘋掉)
 如何讓主程式不要⼀一再重覆寫相同的
 內容?
範例:

   三個物件
(動作定義在類別上)
Project QV086
延續 Project QV084, QV085
產⽣生三個物件
各⾃自移動,兩兩碰撞後反彈
學習重點:更佳的物件寫
法 、取得物件內部的資料
(Property, Synthesize)
哪些東⻄西應該和『物件⾃自⾝身』有關

 物件應該⾃自⼰己動
   速度、⽅方向......
 物件應該具有各⾃自的外觀
   圖檔、⼤大⼩小、顏⾊色......
 物件應該⾃自⼰己偵測碰撞
   撞牆壁後反彈
   兩球相撞後反彈 (???)
MYView.h
   ⾃自定類別的宣告


#import <UIKit/UIKit.h>

@interface MYView : UIView
{
    UIImage *img;
    float incX, incY;
}
@property float incX, incY;

- (id) initImage:(CGRect)frame filename:(NSString *)name;

- (void) move;

@end

                               移動的程式
MYView.m (1/2)

#import "MYView.h"

@implementation MYView
@synthesize incX, incY;

- (id) initImage:(CGRect)frame filename:(NSString *)name
{
    if(self = [super initWithFrame:frame])
    {
        img = [UIImage imageNamed:name];
    }

    incX = 0.5 + arc4random() % 4;
    incY = 0.5 + arc4random() % 4;

    return self;
}                                           每個球的移動速
****** 省略部份程式 ******                        度及⽅方向為隨機

@end
MYView.m (2/2)



****** 省略部份程式 ******
                                 物件內的屬性
- (void) move
{
    // 取得物件,移⾄至新位置
    self.center = CGPointMake(self.center.x+incX, self.center.y+incY);

     // 檢查碰撞到周圍
     if(self.center.x>=(320-self.frame.size.width/2) ||
        self.center.x<=(0+self.frame.size.width/2))
     {
         incX *= -1;
     }

     if (self.center.y>=(460-self.frame.size.height/2) ||
         self.center.y<=(0+self.frame.size.height/2))
     {
         incY *= -1;
     }
 }                              有幾個數值的⽤用法不是那麼地漂亮?
ViewController.h




#import <UIKit/UIKit.h>
#import "MYView.h"

@interface ViewController : UIViewController
{
    MYView *imageView;
    MYView *ball1, *ball2;
}

@end
                                     宣告三個物件
ViewController.m (1/5)
- (void)viewDidLoad
{
    // 產⽣生 MYView 並初始化
    imageView = [[MYView alloc]
                  initImage:CGRectMake(100.0f, 200.0f, 64.0f, 64.0f)
                  filename:@"3d_ball.png"];
    // 背景設為透明
    imageView.backgroundColor = [UIColor clearColor];
    // 將 MYView 新增於主視圖
    [self.view addSubview:imageView];

    // 產⽣生 ball2 並初始化
     ball1 = [[MYView alloc]
               initImage:CGRectMake(10.0f, 280.0f, 64.0f, 64.0f)
                filename:@"3d_ball.png"];
     ball1.backgroundColor = [UIColor clearColor];
     [self.view addSubview:ball1];

     // 產⽣生 ball2 並初始化
     ball2 = [[MYView alloc]
               initImage:CGRectMake(200.0f, 120.0f, 64.0f, 64.0f)
                filename:@"3d_ball.png"];
     ball2.backgroundColor = [UIColor clearColor];
     [self.view addSubview:ball2];

     ****** 省略部份程式 ******
}
ViewController.m (2/5)


    程式主要迴圈:球移動
    多呼叫物件內的 method



- (void) gameLoop: (NSTimer *)theTimer
{
    [imageView move];
    [ball1 move];
    [ball2 move];

    ****** 省略部份程式 ******

}
碰撞處理⽅方法之⼀一                                    ViewController.m (3/5)



- (void) gameLoop: (NSTimer *)theTimer
{
    ****** 省略部份程式 ******

     // 處理碰撞 (必須知道各物件內的屬性)
      float dist = sqrtf(powf(ball1.center.x-ball2.center.x, 2)+
                         powf(ball1.center.y-ball2.center.y, 2));

     if(dist<=(ball1.frame.size.width/2+ball2.frame.size.width/2))
     {
        // 能量守恆的⽅方式
        float incX1Temp = ball1.incX;
        float incY1Temp = ball1.incY;
        float incX2Temp = ball2.incX;           取得各物件內的值,
        float incY2Temp = ball2.incY;
                                                   在主程式中寫
          ball1.incX   =   incX2Temp;
          ball1.incY   =   incY2Temp;
          ball2.incX   =   incX1Temp;
          ball2.incY   =   incY1Temp;
      }
}
碰撞處理⽅方法之⼆二                                      ViewController.m (4/5)

- (BOOL) checkHit: (MYView *)b1 :(MYView *)b2
{
    float dist = sqrtf(powf(b1.center.x-b2.center.x, 2)
                       +powf(b1.center.y-b2.center.y, 2));
    if(dist<=(b1.frame.size.width/2+b2.frame.size.width/2))
    {
        ****** 省略部份程式 (反彈轉向) ******
        return YES;
    }
    else
    {
        return NO;
    }
}


- (void) gameLoop: (NSTimer *)theTimer
{
    // 處理碰撞 (呼叫method傳回是否碰撞)
    if([self checkHit:ball1 :ball2])            傳回兩物件是否碰撞
    {
         ****** 省略部份程式 ******
    }
}
碰撞處理⽅方法之三                                         ViewController.m (5/5)


 - (void) checkHitAndBounce:(MYView *)b1 :(MYView *)b2
 {
     float dist = sqrtf(powf(b1.center.x-b2.center.x, 2)
                        +powf(b1.center.y-b2.center.y, 2));
     if(dist<=(b1.frame.size.width/2+b2.frame.size.width/2))
     {
         // 反彈轉向
         float incX1Temp = b1.incX; float incY1Temp = b1.incY;
         float incX2Temp = b2.incX; float incY2Temp = b2.incY;
         b1.incX = incX2Temp; b1.incY = incY2Temp;
         b2.incX = incX1Temp; b2.incY = incY1Temp;
     }
 }

 - (void) gameLoop: (NSTimer *)theTimer
 {                                                      較完整的功能
     [imageView move];                                   檢查碰撞並
     [ball1 move];
     [ball2 move];                                      ⽴立即處理反彈

     [self checkHitAndBounce:ball1 :ball2];
     [self checkHitAndBounce:ball1 :imageView];
     [self checkHitAndBounce:ball2 :imageView];
 }
程式思考



 物件之間的溝通還有別的做法嗎?
範例:(陣列)

    處理更多球的
   移動、產⽣生及消失
Project QV087
延續 Project QV084~QV086
⾃自訂兩個類別,多個物件
Objective-C 陣列使⽤用
球碰撞炸彈後消失,另有按
鈕產⽣生新的球
學習重點:Objective-C 陣列
當『物件』數量更多時......

 需記錄每⼀一個物件的狀態及資訊
   陣列⼤大⼩小......
   陣列元素的存取......
 數量的變更
   刪除......
   增加......
MYView.h
#import <UIKit/UIKit.h>

@interface MYView : UIView
{
    UIImage *img;
    float incX, incY;
}
@property float incX, incY;

- (id) initImage:(CGRect)frame filename:(NSString *)name;
- (void) move;

@end
                                                 同⼀一個檔案內含
                                                 兩個類別的宣告
@interface MYBomb : UIView
{
    UIImage *img;
}

- (id) initImage:(CGRect)frame filename:(NSString *)name;

@end
#import "MYView.h"
                                                        MYView.m (1/2)
#define   ARC4RANDOM_MAX   0x100000000

@implementation MYView
@synthesize incX, incY;

- (id) initImage:(CGRect)frame filename:(NSString *)name
{
    if(self = [super initWithFrame:frame])
    {
        img = [UIImage imageNamed:name];
    }

    incX = (float)arc4random()/ARC4RANDOM_MAX * 4.0f;
    incY = (float)arc4random()/ARC4RANDOM_MAX * 4.0f;

    return self;
}

- (void)drawRect:(CGRect)rect
{                                                  球的移動量
    ****** 省略部分程式 ******
}                                               浮點數隨機亂數
- (void) move
{
    ****** 省略部分程式 ******
}

@end
MYView.m (2/2)

@implementation MYBomb

- (id) initImage:(CGRect)frame filename:(NSString *)name
{
    if(self = [super initWithFrame:frame])
    {
        img = [UIImage imageNamed:name];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    CGPoint p;
    p.x = 0;
    p.y = 0;
                                          單純擺上⼀一個圖⽚片
    [img drawAtPoint:p];
}

@end
ViewController.h



#import <UIKit/UIKit.h>
#import "MYView.h"

extern const int BallNumber;                   初始陣列⼤大⼩小
@interface ViewController : UIViewController
{
    MYBomb *imageBomb;

    NSMutableArray *ballArray;                 宣告可變內容
}
                                                 之陣列
- (IBAction) buildOne:(id)sender;

@end
ViewController.m (1/4)
    起始多個物件,存於陣列內

- (void)viewDidLoad
{
    ****** 省略部份程式 ******

    // 產⽣生 ballAry
    ballArray = [[NSMutableArray alloc] init];
    for(int i=0; i<BallNumber; i++)
    {
        MYView *tempView = [[MYView alloc]
                initImage:CGRectMake(150.0f, 10.0f, 64.0f, 64.0f)
                 filename:@"3d_ball.png"];
        tempView.backgroundColor = [UIColor clearColor];
        [ballArray addObject:tempView];
    }

    // 從陣列內產⽣生物件
    for(MYView *obj in ballArray)
    {
        [self.view addSubview:obj];
    }

    ****** 省略部份程式 ******
}
ViewController.m (2/4)
陣列內各個球的移動及碰撞處理

- (void) gameLoop: (NSTimer *)theTimer
{
    NSMutableArray *objectsToDelete = [NSMutableArray array];
    // 移動
    for(MYView *obj in ballArray)
    {
        [obj move];

       // 檢查碰指到炸彈的處理
       float dist = sqrtf(powf(obj.center.x-imageBomb.center.x, 2)
                         +powf(obj.center.y-imageBomb.center.y, 2));
       if(dist<(obj.frame.size.width/2+imageBomb.frame.size.width/2))
       {
           // 碰撞後刪除球
           [obj removeFromSuperview];
           // [ballArray removeObject:obj]; // 注意:此為錯誤⽤用法
           [objectsToDelete addObject:obj];
       }
    }
    [ballArray removeObjectsInArray:objectsToDelete];

     ****** 省略部份程式 ******
}
ViewController.m (3/4)
    球與球的碰撞處理



- (void) gameLoop: (NSTimer *)theTimer
{
                                                   取得兩兩不同的
    ****** 省略部份程式 ******                             的陣列元素
    // 兩球間之碰撞檢查及處理

    for(int i=0; i<[ballArray count]-1; i++)
    {
        for(int j=i+1; j<[ballArray count]; j++)
        {
            MYView *obj1 = [ballArray objectAtIndex:i];
            MYView *obj2 = [ballArray objectAtIndex:j];
            [self checkHitAndBounce:obj1 :obj2];
        }
    }

}
ViewController.m (4/4)
       產⽣生新的球



- (IBAction) buildOne:(id)sender
{
    MYView *newView = [[MYView alloc]
                      initImage:CGRectMake(50.0f, 50.0f, 64.0f, 64.0f)
                       filename:@"3d_ball.png"];

    newView.backgroundColor = [UIColor clearColor];

    [ballArray addObject:newView];
                                                  陣列內增加元素
    [self.view addSubview:newView];
}
                                                      置於畫⾯面上
數種繪圖及動畫 API

UIKit

   Quartz 2D

        OpenGL ES
Quartz 2D
painter's model


• canvas
• page
• context
output device
Current transformation
        matrix (CTM)
•   Quartz accomplishes device independence with a
    separate coordinate system - user space -
    mapping it to the coordinate system of the output
    device - device space - using the current
    transformation matrix, or CTM.
•   The current transformation matrix is a particular
    type of matrix called an affine transform, which
    maps points from one coordinate space to
    another by applying translation, rotation, and
    scaling operations. (move, rotate, resize)
Project QV136

 Quartz2D
 基本繪圖指令
 動畫精靈 (sprite) 概念
 應標變換
Quartz2D 繪圖程式的基本架構                         Shape1View.m (1/2)

- (void)drawRect:(CGRect)rect
{
    // 取得圖像內⽂文,並保存它的狀態
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);

    // 重設轉換設定
    CGAffineTransform t = CGContextGetCTM(context);
    t = CGAffineTransformInvert(t);
    CGContextConcatCTM(context, t);

    // 繪圖
    CGContextBeginPath(context);
    CGContextSetRGBFillColor(context, 0.0f, 0.0f, 1.0f, 1.0f);
    CGContextAddRect(context, CGRectMake(0.0f, 0.0f, 100.0f, 150.0f));
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);

    ****** 省略部分程式 ******

    // 還原圖像內⽂文
    CGContextRestoreGState(context);
}
Shape1View.m (2/2)

// 繪圖
CGContextBeginPath(context);
CGContextSetRGBFillColor(context, 0.0f, 0.0f, 1.0f, 1.0f);
CGContextAddRect(context, CGRectMake(0.0f, 0.0f, 100.0f, 150.0f));
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFill);

// 繪圖
CGContextBeginPath(context);
CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 1.0f);
CGContextMoveToPoint(context, 0.0f, 150.0f);
CGContextAddLineToPoint(context, 100.0f, 150.0f);
CGContextAddLineToPoint(context, 50.0f, 200.0f);
CGContextAddLineToPoint(context, 0.0f, 150.0f);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFill);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFill);
ViewController.m
#import "ViewController.h"

#import "Shape1View.h"
#import "Shape2View.h"
#import "Shape3View.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    Shape1View *shape1 = [[Shape1View alloc]
                           initWithFrame:self.view.frame];
    shape1.backgroundColor = [UIColor clearColor];
    [self.view addSubview:shape1];

    ****** 省略部分程式 ******
}
Shape2View
加⼊入Timer及按下互動




  Shape3View
  加⼊入座標變換
Project QV137

動畫精靈 (sprite) 概念
  Quartz 2D(含座標變換)
  UIView (含 NSTimer)
在主迴圈和 sprite 中的觸控
⽐比較
按空⽩白處
會產⽣生新物件




          按物體會變換顏⾊色
          按三次後會消失
ShapeView.h



@interface ShapeView : UIView
{
    NSTimer *timer;
    CGFloat x, y, r;
    CGFloat colorR, colorG, colorB;
    CGFloat rotation, rotationInc;
    int cnt;
}
- (id)initWithFrame:(CGRect)frame                  ShapeView.m (1/2)
{
    self = [super initWithFrame:frame];
    if (self)
    {
        timer = [NSTimer scheduledTimerWithTimeInterval:1/30.0 target:self
                         selector:@selector(move) userInfo:nil repeats:YES];

        r = 80.0f;
        x = 0.0f;
        y = 0.0f;
        colorR = (CGFloat)(arc4random() % 100)/100.0f;
        colorG = (CGFloat)(arc4random() % 100)/100.0f;
        colorB = (CGFloat)(arc4random() % 100)/100.0f;

        rotation = 0.0f;
        rotationInc = (CGFloat)(arc4random() % 100)/100.0f / 10.0f;

        cnt = 0;
    }
    return self;
}
                                           會⾃自⼰己動的動畫精靈
-(void) move
{
    rotation += rotationInc;

    [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
    // 取得圖像內⽂文,並保存它的狀態
                                                   ShapeView.m
    CGContextRef context = UIGraphicsGetCurrentContext();
                                                                     (2/2)
    CGContextSaveGState(context);

    // 重設轉換設定
    CGAffineTransform t = CGContextGetCTM(context);
    t = CGAffineTransformInvert(t);
    t = CGAffineTransformTranslate(t, x+r/2, y+r/2);
    t = CGAffineTransformRotate(t, rotation);
    //t = CGAffineTransformScale (t, sinf(rotation), sinf(rotation));

    CGContextConcatCTM(context, t);

    // 繪圖 (與座標位置無關)
    CGContextBeginPath(context);
    CGContextSetRGBFillColor(context, colorR, colorG, colorB, 1.0f);
    CGContextAddEllipseInRect(context, CGRectMake(x-r/2, y-r/2, r, r));
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);

    CGFloat r2 = r / sqrtf(2.0f);
    CGContextBeginPath(context);
    CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.3f);
    CGContextAddRect(context, CGRectMake(x-r2/2, y-r2/2, r2, r2));
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);

    // 還原圖像內⽂文
    CGContextRestoreGState(context);
}
-(void) touchesBegan:(NSSet *)touches   withEvent:(UIEvent *)event
{
    colorR = (CGFloat)(arc4random() %   100)/100.0f;
    colorG = (CGFloat)(arc4random() %   100)/100.0f;
    colorB = (CGFloat)(arc4random() %   100)/100.0f;

    cnt ++;
    if(cnt==3)
    {                                                   ShapeView.m
        [self removeFromSuperview];
    }
}

                                                       ViewController.m
     此為兩個不同物件的 touch event

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *myTouch = [touches anyObject];
    CGPoint point = [myTouch locationInView:self.view];

    ShapeView *shape = [[ShapeView alloc]
          initWithFrame:CGRectMake(point.x-40, point.y-40, 80, 80)];
    shape.backgroundColor = [UIColor clearColor];
    [self.view addSubview:shape];
}
範例觀摩:
QuartzFun, GLFun




                   ref: ch.14
範例觀摩:QuartzDemo
............

Mais conteúdo relacionado

Mais procurados

Opencv Stitching_detailed algorithm introduction
Opencv Stitching_detailed algorithm introductionOpencv Stitching_detailed algorithm introduction
Opencv Stitching_detailed algorithm introductionwilliam zhang
 
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPMing-Sian Lin
 
Unity遊戲程式設計 - 2D移動與碰撞處理II
Unity遊戲程式設計 - 2D移動與碰撞處理IIUnity遊戲程式設計 - 2D移動與碰撞處理II
Unity遊戲程式設計 - 2D移動與碰撞處理II吳錫修 (ShyiShiou Wu)
 
Kissy editor开发与设计
Kissy editor开发与设计Kissy editor开发与设计
Kissy editor开发与设计yiming he
 
YUI介绍和使用YUI构建web应用
YUI介绍和使用YUI构建web应用YUI介绍和使用YUI构建web应用
YUI介绍和使用YUI构建web应用Adam Lu
 
Script with engine
Script with engineScript with engine
Script with engineWebrebuild
 

Mais procurados (6)

Opencv Stitching_detailed algorithm introduction
Opencv Stitching_detailed algorithm introductionOpencv Stitching_detailed algorithm introduction
Opencv Stitching_detailed algorithm introduction
 
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAPiOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP
 
Unity遊戲程式設計 - 2D移動與碰撞處理II
Unity遊戲程式設計 - 2D移動與碰撞處理IIUnity遊戲程式設計 - 2D移動與碰撞處理II
Unity遊戲程式設計 - 2D移動與碰撞處理II
 
Kissy editor开发与设计
Kissy editor开发与设计Kissy editor开发与设计
Kissy editor开发与设计
 
YUI介绍和使用YUI构建web应用
YUI介绍和使用YUI构建web应用YUI介绍和使用YUI构建web应用
YUI介绍和使用YUI构建web应用
 
Script with engine
Script with engineScript with engine
Script with engine
 

Destaque (12)

I os 07
I os 07I os 07
I os 07
 
I os 08
I os 08I os 08
I os 08
 
I os 11
I os 11I os 11
I os 11
 
I os 06
I os 06I os 06
I os 06
 
I os 02
I os 02I os 02
I os 02
 
I os 03
I os 03I os 03
I os 03
 
I os 15
I os 15I os 15
I os 15
 
課程規畫
課程規畫課程規畫
課程規畫
 
I os 16
I os 16I os 16
I os 16
 
I os 04
I os 04I os 04
I os 04
 
I os 10
I os 10I os 10
I os 10
 
I os 13
I os 13I os 13
I os 13
 

Semelhante a I os 14

15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCellTom Fan
 
06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollViewTom Fan
 
Android 智慧型手機程式設計
Android 智慧型手機程式設計Android 智慧型手機程式設計
Android 智慧型手機程式設計Kyle Lin
 
Unity遊戲程式設計 - 2D運動與碰撞處理I
Unity遊戲程式設計 - 2D運動與碰撞處理IUnity遊戲程式設計 - 2D運動與碰撞處理I
Unity遊戲程式設計 - 2D運動與碰撞處理I吳錫修 (ShyiShiou Wu)
 
Behind Tetris5
Behind Tetris5Behind Tetris5
Behind Tetris5Junwen Sun
 
怎樣在 Flutter app 中使用 Google Maps
怎樣在 Flutter app 中使用 Google Maps怎樣在 Flutter app 中使用 Google Maps
怎樣在 Flutter app 中使用 Google MapsWeizhong Yang
 
11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationControllerTom Fan
 
05 MapKit and Text Input
05 MapKit and Text Input05 MapKit and Text Input
05 MapKit and Text InputTom Fan
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejsChi-wen Sun
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code cleanmacrochen
 
07 View Controllers
07 View Controllers07 View Controllers
07 View ControllersTom Fan
 
2016輕鬆開發自有網路地圖工作坊 進階班 0701
2016輕鬆開發自有網路地圖工作坊 進階班 07012016輕鬆開發自有網路地圖工作坊 進階班 0701
2016輕鬆開發自有網路地圖工作坊 進階班 0701family
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View ControllerTom Fan
 
twMVC#27 | C# 7.0 新功能介紹
twMVC#27 | C# 7.0 新功能介紹twMVC#27 | C# 7.0 新功能介紹
twMVC#27 | C# 7.0 新功能介紹twMVC
 
使用 Java 上的 future/promise API
使用 Java 上的 future/promise  API使用 Java 上的 future/promise  API
使用 Java 上的 future/promise APIkoji lin
 

Semelhante a I os 14 (20)

15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
 
I os 01
I os 01I os 01
I os 01
 
06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView06 Subclassing UIView and UIScrollView
06 Subclassing UIView and UIScrollView
 
Android 智慧型手機程式設計
Android 智慧型手機程式設計Android 智慧型手機程式設計
Android 智慧型手機程式設計
 
Unity遊戲程式設計 - 2D運動與碰撞處理I
Unity遊戲程式設計 - 2D運動與碰撞處理IUnity遊戲程式設計 - 2D運動與碰撞處理I
Unity遊戲程式設計 - 2D運動與碰撞處理I
 
Behind Tetris5
Behind Tetris5Behind Tetris5
Behind Tetris5
 
Ios
IosIos
Ios
 
Roll a ball遊戲專案
Roll a ball遊戲專案Roll a ball遊戲專案
Roll a ball遊戲專案
 
Banquet 52
Banquet 52Banquet 52
Banquet 52
 
怎樣在 Flutter app 中使用 Google Maps
怎樣在 Flutter app 中使用 Google Maps怎樣在 Flutter app 中使用 Google Maps
怎樣在 Flutter app 中使用 Google Maps
 
11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationController
 
05 MapKit and Text Input
05 MapKit and Text Input05 MapKit and Text Input
05 MapKit and Text Input
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code clean
 
005
005005
005
 
07 View Controllers
07 View Controllers07 View Controllers
07 View Controllers
 
2016輕鬆開發自有網路地圖工作坊 進階班 0701
2016輕鬆開發自有網路地圖工作坊 進階班 07012016輕鬆開發自有網路地圖工作坊 進階班 0701
2016輕鬆開發自有網路地圖工作坊 進階班 0701
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller
 
twMVC#27 | C# 7.0 新功能介紹
twMVC#27 | C# 7.0 新功能介紹twMVC#27 | C# 7.0 新功能介紹
twMVC#27 | C# 7.0 新功能介紹
 
使用 Java 上的 future/promise API
使用 Java 上的 future/promise  API使用 Java 上的 future/promise  API
使用 Java 上的 future/promise API
 

Mais de 信嘉 陳

Mais de 信嘉 陳 (12)

Processing 06
Processing 06Processing 06
Processing 06
 
Processing 05
Processing 05Processing 05
Processing 05
 
Processing 04
Processing 04Processing 04
Processing 04
 
Processing 03
Processing 03Processing 03
Processing 03
 
Processing 02
Processing 02Processing 02
Processing 02
 
Processing 01
Processing 01Processing 01
Processing 01
 
Processing 09
Processing 09Processing 09
Processing 09
 
Processing 08
Processing 08Processing 08
Processing 08
 
Processing 07
Processing 07Processing 07
Processing 07
 
Google 街景
Google 街景Google 街景
Google 街景
 
社群網站 Facebook
社群網站 Facebook社群網站 Facebook
社群網站 Facebook
 
網路搜尋
網路搜尋網路搜尋
網路搜尋
 

I os 14

  • 2. 課程⼤大綱 • ⽤用 Processing 學習繪圖程式邏輯 • QV081:球移動反彈 • QV082:Objective-C 的物件思維 • Objective-C 類別與⾃自訂類別 • QV080:⾃自訂類別 • QV084:⾃自訂類別,移動碰撞 • QV085:兩個物件互相碰撞 • QV086:三個物件 (動作定義在物件上) • QV087:⽤用陣列處理更多的球 • Quartz 2D • QV136:Quartz 2D • QV137:動畫精靈 (Sprite) • Samples:QuartzFun, GLFun, QuartzDemo
  • 5. 程式結構 • void setup() { // 設定的部份 } • void draw() { // 時間迴圈的部份 }
  • 6. 資料型態 • int • float • 陣列 int[], float[]
  • 7. 起始設定畫布 • size( w, h) • smooth( ) • frameRate( n ) • background( R, G, B )
  • 8. 常⽤用的畫圖指令 • point(x, y) • line(x1, y1, x2, y2) • rect(x1, y1, x2, y2) • ellipse(x1, y1, w, h) • triangle(x1, y1, x2, y2, x3, y3) • quad(x1, y1, x2, y2, x3, y3, x4, y4) • bezier(x1, y1, x2, y2, x3, y3, x4, y4)
  • 9. 顏⾊色指令 • fill( R, G, B ) • noFill() • stroke( R, G, B) • strokeWeight( w ) • noStroke()
  • 10.
  • 11.
  • 14. Project QV081 球移動,碰到周圍反彈 計時器 NSTimer UI 物件移動位置 常數定義與變數使⽤用 .....傳統 C 語⾔言的思維
  • 16. ViewController.h #import <UIKit/UIKit.h> extern const float areaWidth; extern const float areaHeight; 常數宣告 @interface ViewController : UIViewController { IBOutlet UIImageView *ball; float ballMovementX, ballMovementY; // 每次移動的量 } - (void) initializeTimer; - (void) gameLoop: (NSTimer *) theTimer; 變數宣告 @end
  • 17. 常數宣告 ViewController.m (1/2) const float areaWidth = 320; const float areaHeight = 460; @implementation ViewController - (void) initializeTimer { float theInterval = 1.0 / 30.0; [NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:@selector(gameLoop:) userInfo:nil repeats:YES]; } - (void) gameLoop: (NSTimer *) theTimer { ****** 省略部份程式 ****** } - (void)viewDidLoad 計時器使⽤用 { [super viewDidLoad]; (基本遊戲迴圈) // 起始設定 ballMovementX = 4.0; 速度控制之參數 ballMovementY = 4.0; (1) 遞增量 [self initializeTimer]; } (2) 時間間隔
  • 18. ViewController.m (2/2) - (void) gameLoop: (NSTimer *) theTimer { // 取得新位置 float ballX = ball.center.x; float ballY = ball.center.y; ballX += ballMovementX; ballY += ballMovementY; ball.center = CGPointMake(ballX, ballY); 移⾄至新位置 // 檢查碰撞到周圍 if(ballX > areaWidth || ballX < 0) { ballMovementX = -ballMovementX; } if(ballY > areaHeight || ballY < 0) { ballMovementY = -ballMovementY; } }
  • 19. 思考訓練 考慮有哪些參數是適合單獨定義,以 便讓程式的彈性增加 框框⼤大⼩小,位置 遞增量 預設位置、預設⽅方向 ......
  • 20. 讓程式更具彈性 • 定義的資料儘可能獨⽴立,且保持彈性 • 畫⾯面⼤大⼩小 (320x460) ===> 改為常數或變數定義 • 物件⼤大⼩小 (40x40⾃自訂) ===> 抓取物件的實際值
  • 21. Objective-C 希望...... • 能⽤用類別物件時,儘可能⽤用類別 (避開直覺的資料型態) • 不重覆變數,減少記憶體的使⽤用
  • 22. Project QV082 與 Project QV081 ⽐比較... 球移動,碰到周圍反彈 Objective-C 物件的使⽤用 .....Objective-C 的思維
  • 23. ViewController.h #import <UIKit/UIKit.h> extern const CGRect CGRectArea; "物件" 常數宣告 @interface ViewController : UIViewController { IBOutlet UIImageView *ball; CGPoint ballMovement; // 每次移動的量 } @property(nonatomic,retain) IBOutlet UIImageView *ball; - (void) initializeTimer; - (void) gameLoop: (NSTimer *) theTimer; 物件內含 @end 較多資訊 (x, y)
  • 24. #import "ViewController.h" ViewController.m (1/2) @implementation ViewController @synthesize ball; const CGRect CGRectArea = { {10.0f, 10.0f}, {300.0f, 440.0f} }; - (void) initializeTimer { 常數宣告 float theInterval = 1.0 / 30.0; [NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:@selector(gameLoop:) userInfo:nil repeats:YES]; } - (void) gameLoop: (NSTimer *) theTimer { ****** 省略部份程式 ****** } - (void)viewDidLoad { [super viewDidLoad]; 物件變數使⽤用 // 起始設定 ballMovement = CGPointMake(4.0, 4.0); [self initializeTimer]; }
  • 25. ViewController.m (2/2) 移⾄至計算後 - (void) gameLoop: (NSTimer *) theTimer { 的新位置 // 取得新位置 ball.center = CGPointMake(ball.center.x+ballMovement.x, ball.center.y+ballMovement.y); // 檢查碰撞到周圍 if(ball.center.x > (CGRectGetMinX(CGRectArea)-ball.frame.size.width/2) || ball.center.x < (CGRectGetMinX(CGRectArea)+ball.frame.size.width/2)) { ballMovement.x *= -1;; } if(ball.center.y > (CGRectGetMaxY(CGRectArea)-ball.frame.size.height/2) || ball.center.y < (CGRectGetMinY(CGRectArea)+ball.frame.size.height/2)) { ballMovement.y *= -1; } }
  • 32. #import <UIKit/UIKit.h> MYView.h @interface MYView : UIView @end MYView.m #import "MYView.h" @implementation MYView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code 開新檔時預設的內容 } return self; } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during ...... - (void)drawRect:(CGRect)rect { // Drawing code } */ @end
  • 33. MYView.h #import <UIKit/UIKit.h> @interface MYView : UIView property { UIImage *img; } - (id) initImage:(CGRect)frame filename:(NSString *)name; @end method (此例有改寫起始⽅方法, 故需要宣告)
  • 34. MYView.m #import "MYView.h" @implementation MYView - (id) initImage:(CGRect)frame filename:(NSString *)name { if(self = [super initWithFrame:frame]) { img = [UIImage imageNamed:name]; } return self; } - (void)drawRect:(CGRect)rect ⾃自⼰己的程式 { CGPoint p; p.x = 0; p.y = 0; [img drawAtPoint:p]; } @end
  • 35. ViewController.h 在主程式中使⽤用⾃自訂類別 #import <UIKit/UIKit.h> #import "MYView.h"  引⼊入類別 @interface ViewController : UIViewController { MYView *imageView; } @end  使⽤用⾃自訂類別,產⽣生物件
  • 36. ViewController.m #import "ViewController.h" @implementation ViewController 此例中同時⽰示範不藉由 xib 產⽣生畫⾯面的⽅方式 - (void) loadView { 注意 loadView 及 viewDidLoad ⽤用法時機 // 設置主要視圖 self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; // 將背景設為⽩白⾊色 self.view.backgroundColor = [UIColor whiteColor]; // 產⽣生 MYView 並初始化 imageView = [[MYView alloc] initImage:CGRectMake(100.0f, 200.0f, 48.0f, 48.0f) filename:@"bomb.png"]; // 背景設為透明 imageView.backgroundColor = [UIColor clearColor]; // 將 MYView 新增於主視圖 [self.view addSubview:imageView]; } - (void)viewDidLoad { 使⽤用⾃自訂類別 [super viewDidLoad]; ! // Do any additional setup after loading the view, typically from a nib. }
  • 37. ⾃自我練習 在主程式增加功能,讓此類別物件產 ⽣生動作 (例如:讓它移動) 在類別內增加功能 (property, method, ......)
  • 38. 程式參考 範例:讓此炸彈掉落 - (void) loadView { ****** 省略部份程式 ****** [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(gameLoop:) userInfo:nil repeats:YES]; } - (void) gameLoop: (NSTimer *)theTimer { // 取得物件,移⾄至新位置 (往下掉) imageView.center = CGPointMake(imageView.center.x, imageView.center.y+1); if(imageView.center.y>=self.view.frame.size.height) { imageView.center = CGPointMake(imageView.center.x, 0); } } 此處 imageView 為⾃自訂類別的物件
  • 42. MYView.h ⾃自定類別的宣告 #import <UIKit/UIKit.h> @interface MYView : UIView { UIImage *img; } - (id) initImage:(CGRect)frame filename:(NSString *)name; @end
  • 43. ⾃自定類別的實作 MYView.m #import "MYView.h" @implementation MYView - (id) initImage:(CGRect)frame filename:(NSString *)name { if(self = [super initWithFrame:frame]) { img = [UIImage imageNamed:name]; } return self; } - (void)drawRect:(CGRect)rect { CGPoint p; p.x = 0; p.y = 0; [img drawAtPoint:p]; } @end
  • 44. ViewController.h #import <UIKit/UIKit.h> #import "MYView.h" @interface ViewController : UIViewController { MYView *imageView; float incX, incY; } 要移動的量 @end 直接⽤用浮點數
  • 45. ViewController.m (1/2) - (void)viewDidLoad { [super viewDidLoad]; incX = 2.0; 要移動的量 incY = 1.0; // 產⽣生 MYView 並初始化 imageView = [[MYView alloc] initImage:CGRectMake(100.0f, 200.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; // 背景設為透明 imageView.backgroundColor = [UIColor clearColor]; // 將 MYView 新增於主視圖 [self.view addSubview:imageView]; [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(gameLoop:) userInfo:nil repeats:YES]; }
  • 46. 檢查碰撞, ViewController.m (2/2) 若到邊緣則反彈 - (void) gameLoop: (NSTimer *)theTimer { // 取得物件,移⾄至新位置 imageView.center = CGPointMake(imageView.center.x+incX, imageView.center.y+incY); // 檢查碰撞到周圍 if(imageView.center.x >= (self.view.frame.size.width-imageView.frame.size.width/2) || imageView.center.x <= (0+imageView.frame.size.width/2)) { incX *= -1; } if(imageView.center.y >= (self.view.frame.size.height-imageView.frame.size.height/2) || imageView.center.y <= (0+imageView.frame.size.height/2)) { incY *= -1; } }
  • 49. Project QV085 延續 Project QV084 ⾃自訂類別,產⽣生兩個物件 在畫⾯面上移動,撞牆反彈 球與球相撞時亦反彈 學習重點:尚未使⽤用到物件 的優點 (不是很理想的寫法)
  • 51. ViewController.h ⾃自定類別的宣告 #import <UIKit/UIKit.h> #import "MYView.h" @interface ViewController : UIViewController { MYView *imageView1; float incX1, incY1; MYView *imageView2; 宣告兩個物件 float incX2, incY2; } 以及各別物件的移動量 @end (不是很聰明的⽤用法)
  • 52. - (void)viewDidLoad { ViewController.m (1/3) [super viewDidLoad]; // ********** 第⼀一個球 ********** incX1 = 2.0; incY1 = 1.5; // 產⽣生 MYView 並初始化 imageView1 = [[MYView alloc] initImage:CGRectMake(100.0f, 200.0f, 48.0f, 48.0f) filename:@"bb_blue.png"]; // 背景設為透明 imageView1.backgroundColor = [UIColor clearColor]; // 將 MYView 新增於主視圖 [self.view addSubview:imageView1]; 重覆撰寫 // ********** 第⼆二個球 ********** 各別的運動 incX2 = 1.0; incY2 = 2.5; // 產⽣生 MYView 並初始化 imageView2 = [[MYView alloc] initImage:CGRectMake(80.0f, 120.0f, 48.0f, 48.0f) filename:@"bb_red.png"]; // 背景設為透明 imageView2.backgroundColor = [UIColor clearColor]; // 將 MYView 新增於主視圖 [self.view addSubview:imageView2]; ****** 省略部份程式 ****** }
  • 53. ViewController.m (2/3) - (void) gameLoop: (NSTimer *)theTimer { // ********** 第⼀一個球 ********** // 取得物件,移⾄至新位置 imageView1.center = CGPointMake(imageView1.center.x+incX1, imageView1.center.y+incY1); // 檢查碰撞到周圍 if(imageView1.center.x>=(self.view.frame.size.width-imageView1.frame.size.width/2) || imageView1.center.x<=(0+imageView1.frame.size.width/2)) { incX1 *= -1; } if(imageView1.center.y>=(self.view.frame.size.height-imageView1.frame.size.height/2) || imageView1.center.y<=(0+imageView1.frame.size.height/2)) { incY1 *= -1; } // ********** 第⼀一個球 ********** // 取得物件,移⾄至新位置 imageView2.center = CGPointMake(imageView2.center.x+incX2, imageView2.center.y+incY2); // 檢查碰撞到周圍 if(imageView2.center.x>=(self.view.frame.size.width-imageView2.frame.size.width/2) || imageView2.center.x<=(0+imageView2.frame.size.width/2)) { incX2 *= -1; } if(imageView2.center.y>=(self.view.frame.size.height-imageView2.frame.size.height/2) || imageView2.center.y<=(0+imageView2.frame.size.height/2)) { incY2 *= -1; } ****** 省略部份程式 ****** }
  • 54. - (void) gameLoop: (NSTimer *)theTimer ViewController.m (1/3) { ****** 省略部份程式 ****** // 處理碰撞 float dist = sqrtf(powf(imageView1.center.x-imageView2.center.x, 2) +powf(imageView1.center.y-imageView2.center.y, 2)); if(dist<=(imageView1.frame.size.width/2+imageView2.frame.size.width/2)) { // 不是很漂亮的⽅方法 /* incX1 *= -1; incY1 *= -1; 檢查是否碰撞 incX2 *= -1; 直接反向 incY2 *= -1; (兩球圓⼼心的距離是否 */ ⼩小於兩圓半徑之和) // 能量守恆的⽅方式 float incX1Temp = incX1; float incX2Temp = incX2; float incY1Temp = incY1; float incY2Temp = incY2; incX1 = incX2Temp; 沒什麼了不起的 incY1 = incY2Temp; 物理原理 incX2 = incX1Temp; incY2 = incY1Temp; } }
  • 55. 程式思考 出現更多個球 (希望不會寫到瘋掉) 如何讓主程式不要⼀一再重覆寫相同的 內容?
  • 56. 範例: 三個物件 (動作定義在類別上)
  • 57. Project QV086 延續 Project QV084, QV085 產⽣生三個物件 各⾃自移動,兩兩碰撞後反彈 學習重點:更佳的物件寫 法 、取得物件內部的資料 (Property, Synthesize)
  • 58. 哪些東⻄西應該和『物件⾃自⾝身』有關 物件應該⾃自⼰己動 速度、⽅方向...... 物件應該具有各⾃自的外觀 圖檔、⼤大⼩小、顏⾊色...... 物件應該⾃自⼰己偵測碰撞 撞牆壁後反彈 兩球相撞後反彈 (???)
  • 59. MYView.h ⾃自定類別的宣告 #import <UIKit/UIKit.h> @interface MYView : UIView { UIImage *img; float incX, incY; } @property float incX, incY; - (id) initImage:(CGRect)frame filename:(NSString *)name; - (void) move; @end 移動的程式
  • 60. MYView.m (1/2) #import "MYView.h" @implementation MYView @synthesize incX, incY; - (id) initImage:(CGRect)frame filename:(NSString *)name { if(self = [super initWithFrame:frame]) { img = [UIImage imageNamed:name]; } incX = 0.5 + arc4random() % 4; incY = 0.5 + arc4random() % 4; return self; } 每個球的移動速 ****** 省略部份程式 ****** 度及⽅方向為隨機 @end
  • 61. MYView.m (2/2) ****** 省略部份程式 ****** 物件內的屬性 - (void) move { // 取得物件,移⾄至新位置 self.center = CGPointMake(self.center.x+incX, self.center.y+incY); // 檢查碰撞到周圍 if(self.center.x>=(320-self.frame.size.width/2) || self.center.x<=(0+self.frame.size.width/2)) { incX *= -1; } if (self.center.y>=(460-self.frame.size.height/2) || self.center.y<=(0+self.frame.size.height/2)) { incY *= -1; } } 有幾個數值的⽤用法不是那麼地漂亮?
  • 62. ViewController.h #import <UIKit/UIKit.h> #import "MYView.h" @interface ViewController : UIViewController { MYView *imageView; MYView *ball1, *ball2; } @end 宣告三個物件
  • 63. ViewController.m (1/5) - (void)viewDidLoad { // 產⽣生 MYView 並初始化 imageView = [[MYView alloc] initImage:CGRectMake(100.0f, 200.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; // 背景設為透明 imageView.backgroundColor = [UIColor clearColor]; // 將 MYView 新增於主視圖 [self.view addSubview:imageView]; // 產⽣生 ball2 並初始化 ball1 = [[MYView alloc] initImage:CGRectMake(10.0f, 280.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; ball1.backgroundColor = [UIColor clearColor]; [self.view addSubview:ball1]; // 產⽣生 ball2 並初始化 ball2 = [[MYView alloc] initImage:CGRectMake(200.0f, 120.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; ball2.backgroundColor = [UIColor clearColor]; [self.view addSubview:ball2]; ****** 省略部份程式 ****** }
  • 64. ViewController.m (2/5) 程式主要迴圈:球移動 多呼叫物件內的 method - (void) gameLoop: (NSTimer *)theTimer { [imageView move]; [ball1 move]; [ball2 move]; ****** 省略部份程式 ****** }
  • 65. 碰撞處理⽅方法之⼀一 ViewController.m (3/5) - (void) gameLoop: (NSTimer *)theTimer { ****** 省略部份程式 ****** // 處理碰撞 (必須知道各物件內的屬性) float dist = sqrtf(powf(ball1.center.x-ball2.center.x, 2)+ powf(ball1.center.y-ball2.center.y, 2)); if(dist<=(ball1.frame.size.width/2+ball2.frame.size.width/2)) { // 能量守恆的⽅方式 float incX1Temp = ball1.incX; float incY1Temp = ball1.incY; float incX2Temp = ball2.incX; 取得各物件內的值, float incY2Temp = ball2.incY; 在主程式中寫 ball1.incX = incX2Temp; ball1.incY = incY2Temp; ball2.incX = incX1Temp; ball2.incY = incY1Temp; } }
  • 66. 碰撞處理⽅方法之⼆二 ViewController.m (4/5) - (BOOL) checkHit: (MYView *)b1 :(MYView *)b2 { float dist = sqrtf(powf(b1.center.x-b2.center.x, 2) +powf(b1.center.y-b2.center.y, 2)); if(dist<=(b1.frame.size.width/2+b2.frame.size.width/2)) { ****** 省略部份程式 (反彈轉向) ****** return YES; } else { return NO; } } - (void) gameLoop: (NSTimer *)theTimer { // 處理碰撞 (呼叫method傳回是否碰撞) if([self checkHit:ball1 :ball2]) 傳回兩物件是否碰撞 { ****** 省略部份程式 ****** } }
  • 67. 碰撞處理⽅方法之三 ViewController.m (5/5) - (void) checkHitAndBounce:(MYView *)b1 :(MYView *)b2 { float dist = sqrtf(powf(b1.center.x-b2.center.x, 2) +powf(b1.center.y-b2.center.y, 2)); if(dist<=(b1.frame.size.width/2+b2.frame.size.width/2)) { // 反彈轉向 float incX1Temp = b1.incX; float incY1Temp = b1.incY; float incX2Temp = b2.incX; float incY2Temp = b2.incY; b1.incX = incX2Temp; b1.incY = incY2Temp; b2.incX = incX1Temp; b2.incY = incY1Temp; } } - (void) gameLoop: (NSTimer *)theTimer { 較完整的功能 [imageView move]; 檢查碰撞並 [ball1 move]; [ball2 move]; ⽴立即處理反彈 [self checkHitAndBounce:ball1 :ball2]; [self checkHitAndBounce:ball1 :imageView]; [self checkHitAndBounce:ball2 :imageView]; }
  • 69. 範例:(陣列) 處理更多球的 移動、產⽣生及消失
  • 70. Project QV087 延續 Project QV084~QV086 ⾃自訂兩個類別,多個物件 Objective-C 陣列使⽤用 球碰撞炸彈後消失,另有按 鈕產⽣生新的球 學習重點:Objective-C 陣列
  • 71. 當『物件』數量更多時...... 需記錄每⼀一個物件的狀態及資訊 陣列⼤大⼩小...... 陣列元素的存取...... 數量的變更 刪除...... 增加......
  • 72. MYView.h #import <UIKit/UIKit.h> @interface MYView : UIView { UIImage *img; float incX, incY; } @property float incX, incY; - (id) initImage:(CGRect)frame filename:(NSString *)name; - (void) move; @end 同⼀一個檔案內含 兩個類別的宣告 @interface MYBomb : UIView { UIImage *img; } - (id) initImage:(CGRect)frame filename:(NSString *)name; @end
  • 73. #import "MYView.h" MYView.m (1/2) #define ARC4RANDOM_MAX 0x100000000 @implementation MYView @synthesize incX, incY; - (id) initImage:(CGRect)frame filename:(NSString *)name { if(self = [super initWithFrame:frame]) { img = [UIImage imageNamed:name]; } incX = (float)arc4random()/ARC4RANDOM_MAX * 4.0f; incY = (float)arc4random()/ARC4RANDOM_MAX * 4.0f; return self; } - (void)drawRect:(CGRect)rect { 球的移動量 ****** 省略部分程式 ****** } 浮點數隨機亂數 - (void) move { ****** 省略部分程式 ****** } @end
  • 74. MYView.m (2/2) @implementation MYBomb - (id) initImage:(CGRect)frame filename:(NSString *)name { if(self = [super initWithFrame:frame]) { img = [UIImage imageNamed:name]; } return self; } - (void)drawRect:(CGRect)rect { CGPoint p; p.x = 0; p.y = 0; 單純擺上⼀一個圖⽚片 [img drawAtPoint:p]; } @end
  • 75. ViewController.h #import <UIKit/UIKit.h> #import "MYView.h" extern const int BallNumber; 初始陣列⼤大⼩小 @interface ViewController : UIViewController { MYBomb *imageBomb; NSMutableArray *ballArray; 宣告可變內容 } 之陣列 - (IBAction) buildOne:(id)sender; @end
  • 76. ViewController.m (1/4) 起始多個物件,存於陣列內 - (void)viewDidLoad { ****** 省略部份程式 ****** // 產⽣生 ballAry ballArray = [[NSMutableArray alloc] init]; for(int i=0; i<BallNumber; i++) { MYView *tempView = [[MYView alloc] initImage:CGRectMake(150.0f, 10.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; tempView.backgroundColor = [UIColor clearColor]; [ballArray addObject:tempView]; } // 從陣列內產⽣生物件 for(MYView *obj in ballArray) { [self.view addSubview:obj]; } ****** 省略部份程式 ****** }
  • 77. ViewController.m (2/4) 陣列內各個球的移動及碰撞處理 - (void) gameLoop: (NSTimer *)theTimer { NSMutableArray *objectsToDelete = [NSMutableArray array]; // 移動 for(MYView *obj in ballArray) { [obj move]; // 檢查碰指到炸彈的處理 float dist = sqrtf(powf(obj.center.x-imageBomb.center.x, 2) +powf(obj.center.y-imageBomb.center.y, 2)); if(dist<(obj.frame.size.width/2+imageBomb.frame.size.width/2)) { // 碰撞後刪除球 [obj removeFromSuperview]; // [ballArray removeObject:obj]; // 注意:此為錯誤⽤用法 [objectsToDelete addObject:obj]; } } [ballArray removeObjectsInArray:objectsToDelete]; ****** 省略部份程式 ****** }
  • 78. ViewController.m (3/4) 球與球的碰撞處理 - (void) gameLoop: (NSTimer *)theTimer { 取得兩兩不同的 ****** 省略部份程式 ****** 的陣列元素 // 兩球間之碰撞檢查及處理 for(int i=0; i<[ballArray count]-1; i++) { for(int j=i+1; j<[ballArray count]; j++) { MYView *obj1 = [ballArray objectAtIndex:i]; MYView *obj2 = [ballArray objectAtIndex:j]; [self checkHitAndBounce:obj1 :obj2]; } } }
  • 79. ViewController.m (4/4) 產⽣生新的球 - (IBAction) buildOne:(id)sender { MYView *newView = [[MYView alloc] initImage:CGRectMake(50.0f, 50.0f, 64.0f, 64.0f) filename:@"3d_ball.png"]; newView.backgroundColor = [UIColor clearColor]; [ballArray addObject:newView]; 陣列內增加元素 [self.view addSubview:newView]; } 置於畫⾯面上
  • 80. 數種繪圖及動畫 API UIKit Quartz 2D OpenGL ES
  • 82. painter's model • canvas • page • context
  • 84. Current transformation matrix (CTM) • Quartz accomplishes device independence with a separate coordinate system - user space - mapping it to the coordinate system of the output device - device space - using the current transformation matrix, or CTM. • The current transformation matrix is a particular type of matrix called an affine transform, which maps points from one coordinate space to another by applying translation, rotation, and scaling operations. (move, rotate, resize)
  • 85. Project QV136 Quartz2D 基本繪圖指令 動畫精靈 (sprite) 概念 應標變換
  • 86. Quartz2D 繪圖程式的基本架構 Shape1View.m (1/2) - (void)drawRect:(CGRect)rect { // 取得圖像內⽂文,並保存它的狀態 CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); // 重設轉換設定 CGAffineTransform t = CGContextGetCTM(context); t = CGAffineTransformInvert(t); CGContextConcatCTM(context, t); // 繪圖 CGContextBeginPath(context); CGContextSetRGBFillColor(context, 0.0f, 0.0f, 1.0f, 1.0f); CGContextAddRect(context, CGRectMake(0.0f, 0.0f, 100.0f, 150.0f)); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill); ****** 省略部分程式 ****** // 還原圖像內⽂文 CGContextRestoreGState(context); }
  • 87. Shape1View.m (2/2) // 繪圖 CGContextBeginPath(context); CGContextSetRGBFillColor(context, 0.0f, 0.0f, 1.0f, 1.0f); CGContextAddRect(context, CGRectMake(0.0f, 0.0f, 100.0f, 150.0f)); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill); // 繪圖 CGContextBeginPath(context); CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 1.0f); CGContextMoveToPoint(context, 0.0f, 150.0f); CGContextAddLineToPoint(context, 100.0f, 150.0f); CGContextAddLineToPoint(context, 50.0f, 200.0f); CGContextAddLineToPoint(context, 0.0f, 150.0f); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill);
  • 88. ViewController.m #import "ViewController.h" #import "Shape1View.h" #import "Shape2View.h" #import "Shape3View.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; Shape1View *shape1 = [[Shape1View alloc] initWithFrame:self.view.frame]; shape1.backgroundColor = [UIColor clearColor]; [self.view addSubview:shape1]; ****** 省略部分程式 ****** }
  • 90. Project QV137 動畫精靈 (sprite) 概念 Quartz 2D(含座標變換) UIView (含 NSTimer) 在主迴圈和 sprite 中的觸控 ⽐比較
  • 91. 按空⽩白處 會產⽣生新物件 按物體會變換顏⾊色 按三次後會消失
  • 92. ShapeView.h @interface ShapeView : UIView { NSTimer *timer; CGFloat x, y, r; CGFloat colorR, colorG, colorB; CGFloat rotation, rotationInc; int cnt; }
  • 93. - (id)initWithFrame:(CGRect)frame ShapeView.m (1/2) { self = [super initWithFrame:frame]; if (self) { timer = [NSTimer scheduledTimerWithTimeInterval:1/30.0 target:self selector:@selector(move) userInfo:nil repeats:YES]; r = 80.0f; x = 0.0f; y = 0.0f; colorR = (CGFloat)(arc4random() % 100)/100.0f; colorG = (CGFloat)(arc4random() % 100)/100.0f; colorB = (CGFloat)(arc4random() % 100)/100.0f; rotation = 0.0f; rotationInc = (CGFloat)(arc4random() % 100)/100.0f / 10.0f; cnt = 0; } return self; } 會⾃自⼰己動的動畫精靈 -(void) move { rotation += rotationInc; [self setNeedsDisplay]; }
  • 94. - (void)drawRect:(CGRect)rect { // 取得圖像內⽂文,並保存它的狀態 ShapeView.m CGContextRef context = UIGraphicsGetCurrentContext(); (2/2) CGContextSaveGState(context); // 重設轉換設定 CGAffineTransform t = CGContextGetCTM(context); t = CGAffineTransformInvert(t); t = CGAffineTransformTranslate(t, x+r/2, y+r/2); t = CGAffineTransformRotate(t, rotation); //t = CGAffineTransformScale (t, sinf(rotation), sinf(rotation)); CGContextConcatCTM(context, t); // 繪圖 (與座標位置無關) CGContextBeginPath(context); CGContextSetRGBFillColor(context, colorR, colorG, colorB, 1.0f); CGContextAddEllipseInRect(context, CGRectMake(x-r/2, y-r/2, r, r)); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill); CGFloat r2 = r / sqrtf(2.0f); CGContextBeginPath(context); CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.3f); CGContextAddRect(context, CGRectMake(x-r2/2, y-r2/2, r2, r2)); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFill); // 還原圖像內⽂文 CGContextRestoreGState(context); }
  • 95. -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { colorR = (CGFloat)(arc4random() % 100)/100.0f; colorG = (CGFloat)(arc4random() % 100)/100.0f; colorB = (CGFloat)(arc4random() % 100)/100.0f; cnt ++; if(cnt==3) { ShapeView.m [self removeFromSuperview]; } } ViewController.m 此為兩個不同物件的 touch event - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *myTouch = [touches anyObject]; CGPoint point = [myTouch locationInView:self.view]; ShapeView *shape = [[ShapeView alloc] initWithFrame:CGRectMake(point.x-40, point.y-40, 80, 80)]; shape.backgroundColor = [UIColor clearColor]; [self.view addSubview:shape]; }