cocos2d 学习 第五章
发布日期:2021-08-24 11:59:48 浏览次数:1 分类:技术文章

本文共 20320 字,大约阅读时间需要 67 分钟。

当你用CCDirector replaceScene方法替换场景时, 每个节点都会调用CCNode所带的三个方法。

这三个方法是:onEnter, onEnterTransitionDidFinish和onExit。 

取决于是否使用了CCTransitionScene, onEnter和onExit会在场景转换过程中的某个时间点被调用。对于这三个方法, 你必须调用它们的super方法以避免触摸输入问题和内存泄漏的问题。 

-(void) onEnter

{

  // 节点调用init方法以后将会调用此方法

  // 如果使用了CCTransitionScene,将会在过渡效果开始以后调用此方法

  [super onEnter];

}

-(void) onEnterTransitionDidFinish{

    // 调用onEnter以后将会调用此方法

    // 如果使用了CCTransitionScene,将会在过渡效果结束以后调用此方法

    [super onEnterTransitionDidFinish];

}-(void) onExit {

    // 节点调用dealloc方法之前将会调用此方法

    // 如果使用了CCTransitionScene,将会在过渡效果结束以后调用此方法

    [super onExit]; 

如果你不在onEnter方法里调用它的super方法的话,你的新场景可能不会对触摸或者加速计的输入有任何反应。

如果你不在onExit方法里调用它的super方法,当前场景可能不会从内存里释放。 

你可以在场景转换之前或者之后,通过使用上述方法在节点中完成一些特定的 操作。因为在程序进入onEnter方法的时候,场景中的所有节点都已经设置完成 了;同时,在onExit方法中,所有节点都还存在于内存中。 

1. scene: OtherScene

2. init: 
3. onEnter: 
4. // ......
5. onExit: 
6. onEnterTransitionDidFinish: 

7. dealloc: 

 

replaceScene -> Other Scene.

===========================================

scene: OtherScene

init: <OtherScene = 0x1835e10 | Tag = -1>

onExit: <FirstScene = 0x436170 | Tag = -1>  // 因为切换到OhterScene时,FirstScene还在内存中,过程是先初始化OhterScene,之后从内存中清空FirstScene

dealloc: <FirstScene = 0x436170 | Tag = -1>

onEnter: <OtherScene = 0x1835e10 | Tag = -1>

onEnterTransitionDidFinish: <OtherScene = 0x1835e10 | Tag = -1>

 

#import <Foundation/Foundation.h>

#import "cocos2d.h"

typedefenum

{

    TargetSceneINVALID = 0,

    TargetSceneFirstScene,

    TargetSceneOtherScene,

    TargetSceneMAX,

}

TargetScenes;

 

@interface LoadingScene : CCScene

{

    TargetScenes targetScene_;

}

+(id)sceneWithTargetScene:(TargetScenes)targetScene;

-(id)initWithTargetScene:(TargetScenes)targetScene;

@end

 

 

#import "LoadingScene.h"

#import "FirstScene.h"

#import "OtherScene.h"

 

@implementation LoadingScene

+(id)sceneWithTargetScene:(TargetScenes)targetScene

{

    return [[[self alloc] initWithTargetScene:targetScene] autorelease];

}

 

-(id)initWithTargetScene:(TargetScenes)targetScene

{

    if((self = [super init]))

    {

        targetScene_ = targetScene;

        CCLabelTTF *label = [CCLabelTTFlabelWithString:@"Loading..."

                                               fontName:@"Marker Felt"

                                               fontSize:64];

        CGSize size = [[CCDirectorsharedDirector] winSize];

        label.position = CGPointMake(size.width*0.5, size.height*0.5);

        [self addChild:label];

        [selfscheduleUpdate];

    }

    returnself;

}

 

-(void)update:(ccTime)delta

{

    [selfunscheduleAllSelectors];

    switch (targetScene_) {

        caseTargetSceneFirstScene:

            [[CCDirectorsharedDirector] replaceScene:[FirstScenescene]];

            break;

        caseTargetSceneOtherScene:

            [[CCDirectorsharedDirector] replaceScene:[OtherScenescene]];

            break;

        default:

            NSAssert2(nil, @"%@:unsupported TargetScene %i", NSStringFromSelector(_cmd), targetScene_);

            break;

    }

}

@end

 

因为 LoadingScene 继承自 CCScene,并且要求传递一个新的参数给它,所以仅 仅调用[CCScene node]是不够的。SceneWithTargetScene 方法首先会完成对 self 的内存分配,然后调用 initWithTargetScene 方法,最后返回一个新的自 动释放对象。 

这里的 init 方法把目标场景储存在一个成员变量里面,接着生成一个 “Loading...”标签,最后运行 scheduleUpdate 这个预约方法。 

为什么我们不在init方法里直接使用replaceScene方法呢?这里有两条规则需要遵守。规则一:永远不要在一个节点的init方法中调用CCDirector的replaceScene方法。

规则二:请遵守规则一。不遵守规则的后果是程序崩溃。Director无法容忍一个节点在初始化的同时进行场景替换。 

在过渡效果中使用LoadingScene可以优化内存的使用:因为你使用了一个简单 的过渡场景用于替换当前场景,然后用最终的目标场景替换这个过渡场景。在 这个替换的过程中,cocos2d将会有足够的时间来释放之前场景所占用的内存。 我们得到的实际效果是:不再会有两个复杂场景同时占用着内存的情况了,因 此在场景转换过程中也就减少了出现内存使用高峰的机会。 

 

1 #import 
2 #import "cocos2d.h" 3 #import "GameLayer.h" 4 #import "UserInterfaceLayer.h" 5 6 typedef enum // 定义二个层的Tag值 7 { 8 LayerTagGameLayer, // 用于标示[游戏层] 9 LayerTagUILayer, // 用于标示[用户设置层]10 } MultiLayerSceneTags;11 12 typedef enum // 定义动作Tag13 {14 ActionTagGameLayerMovesBack,15 ActionTagGameLayerRotates,16 } MultiLayerSceneActionTags;17 18 @interface MultiLayerScene : CCLayer19 {20 bool isToucheForUserInterface; // 判断用户点击的是否为设置界面21 }22 @property (readonly) GameLayer *gameLayer;23 @property (readonly) UserInterfaceLayer *uiLayer;24 25 +(MultiLayerScene *)sharedLayer;26 +(CGPoint)locationFromTouch:(UITouch *)touch;27 +(CGPoint)locationFromTouches:(UITouch *)touches;28 +(id)scene;29 30 @end

 

1 #import "MultiLayerScene.h" 2  3 @implementation MultiLayerScene 4  5 static MultiLayerScene* multiLayerSceneInstance;    // 定义静态的本场景变量 6  7 +(MultiLayerScene *)sharedLayer     // 返回静态场景变量 8 { 9     NSAssert(multiLayerSceneInstance != nil, @"MultiLayerScene not available!");10     return multiLayerSceneInstance;11 }12 13 #pragma mark 返回游戏层14 - (GameLayer *)gameLayer15 {16     CCNode *layer = [self getChildByTag:LayerTagGameLayer];17     NSAssert([layer isKindOfClass:[GameLayer class]], @"%@: not a GameLayer!",NSStringFromSelector(_cmd));18     return (GameLayer *)layer;19 }20 21 #pragma mark 返回用户设置层22 - (UserInterfaceLayer*)uiLayer23 {24     CCNode* layer = [[MultiLayerScene sharedLayer] getChildByTag:LayerTagUILayer];25     NSAssert([layer isKindOfClass:[UserInterfaceLayer class]], @"%@: not a UserInterfaceLayer!", NSStringFromSelector(_cmd));26     return (UserInterfaceLayer *)layer;27 }28 29 #pragma mark 得到用户点击的坐标并转换为OpenGL坐标30 +(CGPoint)locationFromTouch:(UITouch *)touch31 {32     // locationInView:函数返回一个CGPoint类型的值,表示触摸在view这个视图上的位置,这里返回的位置是针对view的坐标系的。33     // 调用时传入的view参数为空的话,返回的时触摸点在整个窗口的位置。34     CGPoint touchLocation = [touch locationInView:[touch view]];35     36     // convertToGL:将ios坐标系转换为OpenGL坐标系,返回类型 CGPoint37     return [[CCDirector sharedDirector] convertToGL:touchLocation];38 }39 40 #pragma mark 如果用户是多点触摸,那么返回其中任意一个触摸点的OpenGL坐标41 +(CGPoint) locationFromTouches:(NSSet *)touches42 {43     return [self locationFromTouch:[touches anyObject]];44 }45 46 #pragma mark - 静态构造方法Scene47 +(id) scene48 {49     CCScene* scene = [CCScene node];50     MultiLayerScene* layer = [MultiLayerScene node];51     [scene addChild:layer];52     return scene;53 }54 55 #pragma mark 初始化方法init56 -(id) init57 {58     if ((self = [super init]))59     {60         NSAssert(multiLayerSceneInstance == nil, @"another MultiLayerScene is already in use!");61         multiLayerSceneInstance = self;62         63         GameLayer* gameLayer = [GameLayer node];64         [self addChild:gameLayer z:1 tag:LayerTagGameLayer];        // 将游戏层加入场景中,Z轴位置为 1,65         // z:是指添加的ZOrder值,ZOrder是指该成员的层级(也可以说深度),z值大的成员在z值小的成员的上面;66         67         UserInterfaceLayer* uiLayer = [UserInterfaceLayer node];    // 将用户设置层加入场景中,Z轴位置为 268         [self addChild:uiLayer z:2 tag:LayerTagUILayer];69     }70     return self;71 }72 73 -(void) dealloc74 {75     CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);76     multiLayerSceneInstance = nil;77     [super dealloc];78 }79 80 @end
1 #import 
2 #import "cocos2d.h" 3 4 #pragma mark 游戏内容层 5 @interface GameLayer : CCLayer 6 { 7 CGPoint gameLayerPosition; 8 CGPoint lastTouchLocation; 9 }10 11 @end

 

1 #import "GameLayer.h"  2 #import "cocos2d.h"  3 #import "MultiLayerScene.h"  4   5 @interface GameLayer (PrivateMethod)  6 -(void)addRandomThings;  7 @end  8   9 @implementation GameLayer 10 -(id)init 11 { 12     if((self = [super init])) 13     { 14         gameLayerPosition = self.position;                                                      // 返回层的默认坐标(0,0) 15         CGSize screenSize = [[CCDirector sharedDirector] winSize]; 16         CCSprite *background = [CCSprite spriteWithFile:@"grass.png"];                          // 载入“小草”精灵 17         background.position = CGPointMake(screenSize.width * 0.5, screenSize.height * 0.5);     // 设置小草精灵的位置 18         [self addChild:background];                                                             // 将小草加入游戏层中 19          20         CCLabelTTF *label = [CCLabelTTF labelWithString:@"GameLayer" 21                                              fontName:@"Marker Felt" 22                                                fontSize:44]; 23         label.color = ccBLACK; 24         label.position = CGPointMake(screenSize.width*0.5, screenSize.height * 0.5); 25         //label.anchorPoint = CGPointMake(0.5f, 0.5f); 26         [self addChild:label];                                                                  // 将文字标签加入游戏层 27          28         [self addRandomThings];                                                              29         self.isTouchEnabled = YES;                                                              // 支持触屏 30     } 31     return self; 32 } 33  34 #pragma mark 在游戏层添加一些随机物品 35 -(void)addRandomThings 36 { 37     CGSize screenSize = [[CCDirector sharedDirector] winSize]; 38      39     for (int i = 0; i<4; i++)                       // 添加4团随机的火元素 40     { 41         CCSprite *firething = [CCSprite spriteWithFile:@"firething.png"]; 42         firething.position = CGPointMake(CCRANDOM_0_1()*screenSize.width, CCRANDOM_0_1()*screenSize.height); 43         [self addChild:firething];                  // 将火元素添加到游戏层中 44         [self runRandomMoveSequence:firething];     // 随机移动此火元素 45     } 46      47     for (int i = 0; i<10; i++)                      // 添加10个随机的蜘蛛 48     { 49         CCSprite *spider = [CCSprite spriteWithFile:@"spider.png"]; 50         spider.position = CGPointMake(CCRANDOM_0_1()*screenSize.width, CCRANDOM_0_1()*screenSize.height); 51         [self addChild:spider]; 52         [self runRandomMoveSequence:spider];        // 随机移动此火元素 53     } 54 } 55 -(void) runRandomMoveSequence:(CCNode*)node 56 { 57     float duration = CCRANDOM_0_1() * 5 + 1; 58      59     CCMoveBy *move1 = [CCMoveBy actionWithDuration:duration position:CGPointMake(-180, 0)];     // 在现在位置水平左移180个单位 60     CCMoveBy* move2 = [CCMoveBy actionWithDuration:duration position:CGPointMake(0, -180)]; 61     CCMoveBy* move3 = [CCMoveBy actionWithDuration:duration position:CGPointMake(180, 0)]; 62     CCMoveBy* move4 = [CCMoveBy actionWithDuration:duration position:CGPointMake(0, 180)]; 63     CCSequence *sequence = [CCSequence actions:move1,move2,move3,move4, nil];                   // 定义一个动作执行序列 64     CCRepeatForever *repeat = [CCRepeatForever actionWithAction:sequence];                      // 该类的作用就是无限期执行某个动作或动作序列,直到被停止 65     [node runAction:repeat]; 66 } 67  68 #pragma mark 在CCLayer中经常要注册Touch Dispatcher来让Layer处理Touch事件。 Dispatcher:调度员 69 -(void)registerWithTouchDispatcher 70 { 71     // 设置此层可以接受手触发事件,且priority(优先级)为0,越小优先级越高,越先接受用户触摸事件,swallow(是否可以吞没事件)为YES 72     [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self 73                                                               priority:0 74                                                        swallowsTouches:YES]; 75 } 76  77 #pragma mark 接受用户触摸事件 78 -(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event 79 { 80     CCLOG(@"GameLayer: ccTouchBegan"); 81     lastTouchLocation = [MultiLayerScene locationFromTouch:touch];  // 得到用户触摸开始点OpenGL坐标 82     [self stopActionByTag:ActionTagGameLayerMovesBack];             // 停止此时可能存在加速动作 83     return YES;                                                     // 如果是NO,那么就结束触摸事件传递,ccTouchMoved、ccTouchEnded将不执行 84      85 } 86  87 #pragma mark 接受用户滑动事件 88 -(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event 89 { 90     CCLOG(@"GameLayer: ccTouchMoved"); 91     CGPoint currentTouchLocaion = [MultiLayerScene locationFromTouch:touch];    // 在移动的过程中得到一系列新的目标点位 92     CGPoint moveTo = ccpSub(lastTouchLocation, currentTouchLocaion);            // 得到二个点相减后的位置 93     moveTo = ccpMult(moveTo, -1);                                               // 将moveTo的X,Y 都与-1相乘,得到原点将移动到新点位的值 94     lastTouchLocation = currentTouchLocaion;                                    // 将现在点位更新为已经使用过的点位 95     self.position = ccpAdd(self.position, moveTo);                              // 将本层原点向moveTo点位移动 96 } 97  98 #pragma mark 用户触摸事件结束后,本层内容恢复到原点 99 -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event100 {101     CCLOG(@"GameLayer: ccTouchEnded");102     CCMoveTo *move = [CCMoveTo actionWithDuration:1 position:gameLayerPosition];        // 返回原点103     CCEaseIn *ease = [CCEaseIn actionWithAction:move rate:0.5f];                        // 在0.5秒内完成move动作,且开始运行时是加速状态的104     ease.tag = ActionTagGameLayerMovesBack;                                             // 此加速动作的Tag = ActionTagGameLayerMovesBack105     [self runAction:ease];106 }107 @end

 

1 #import 
2 #import "cocos2d.h" 3 #pragma mark 用户设置层 4 5 typedef enum 6 { 7 UIlayerTagFrameSprite, 8 } UserInterfaceLayerTags; 9 10 @interface UserInterfaceLayer : CCLayer {11 12 }13 -(BOOL) isTouchMe:(CGPoint)touchLocation;14 @end
1 #import "UserInterfaceLayer.h" 2 #import "MultiLayerScene.h" 3  4 @implementation UserInterfaceLayer 5 -(id)init 6 { 7     if((self = [super init])) 8     { 9         CGSize screenSize = [[CCDirector sharedDirector] winSize];10         CCSprite *uiframe = [CCSprite spriteWithFile:@"ui-frame.png"];11         uiframe.position = CGPointMake(0,screenSize.height);12         uiframe.anchorPoint = CGPointMake(0, 1);13         [self addChild:uiframe z:0 tag:UIlayerTagFrameSprite];                  // 设置图片Tag = UIlayerTagFrameSprite14         15         CCLabelTTF *label = [CCLabelTTF labelWithString:@"这里可以显示程序积分" fontName:@"Courier" fontSize:22];16         label.color = ccBLACK;17         label.position = CGPointMake(screenSize.width * 0.5,screenSize.height);18         label.anchorPoint = CGPointMake(0.5f, 1);19         [self addChild:label];20         self.isTouchEnabled = YES;21     }22     return self;23 }24 25 -(void) registerWithTouchDispatcher26 {27     [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES];28 }29 -(BOOL)isTouchMe:(CGPoint)touchLocation30 {31     CCNode *node = [self getChildByTag:UIlayerTagFrameSprite];              // 查找到tag = UIlayerTagFrameSprite 的精灵对象32     return CGRectContainsPoint([node boundingBox], touchLocation);          // 判断用户触摸点是否在node之中33 }34 35 #pragma mark 当用户触摸标题栏时,gameLayer进行一系列操作36 -(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event37 {38     CGPoint location = [MultiLayerScene locationFromTouch:touch];                       // 得到用户点击的OpenGL坐标39     bool isTouchHandled = [self isTouchMe:location];                                    // 判断用户点击的是否为UserInteface层40     if(isTouchHandled)                                                                  // 如果 YES,那么事件将被吞没不向下层传递了
41     {42         CCNode *node = [self getChildByTag:UIlayerTagFrameSprite];43         NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite");44         ((CCSprite *)node).color = ccRED;                                               // 将UserInterface层显示的字体改为红色45         46         CCScaleTo *scaleDown = [CCScaleTo actionWithDuration:2 scale:0];                // 在2秒内缩放为0,消失了47         CCScaleTo *scaleUp = [CCScaleTo actionWithDuration:2 scale:1];                  // 在2秒内还原为原来大小48         CCSequence *sequence = [CCSequence actions:scaleDown,scaleUp, nil];             // 定义包括二种缩放方式的动作序列49         sequence.tag = ActionTagGameLayerRotates;                                       // 将此序列的Tag = ActionTagGameLayerRotates50         51         GameLayer *gameLayer = [MultiLayerScene sharedLayer].gameLayer;                 // 得到gameLayer层52         CCRotateBy *rotate = [CCRotateBy actionWithDuration:4 angle:360];               // 在4秒内旋转360度53         [gameLayer stopActionByTag:ActionTagGameLayerRotates];                          // 停止可能存在的缩放序列54         [gameLayer setRotation:0];                                                      // 初始化gameLayer层旋转角度为055         [gameLayer setScale:1];                                                         // 初始化gameLayer层缩放比例为1倍,既原始大小56         [gameLayer runAction:rotate];                                                   // 执行旋转57         [gameLayer runAction:sequence];                                                 // 执行缩放58     }59     return isTouchHandled;60 }61 62 #pragma mark 用户触摸结束后恢复标题字体颜色63 -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event64 {65     CCNode *node = [self getChildByTag:UIlayerTagFrameSprite];66     NSAssert([node isKindOfClass:[CCSprite class]], @"node is not a CCSprite");67     ((CCSprite *)node).color = ccWHITE;68 }69 70 -(void)dealloc71 {72     CCLOG(@"%@: %@",NSStringFromSelector(_cmd),self);73     [super dealloc];74 }75 @end

 

1 #import 
2 #import "cocos2d.h" 3 4 // CCTargetedTouchDelegate,CCStandardTouchDelegate 协议将使对象支持触摸事件 5 6 @interface Spider : NSObject
7 { 8 CCSprite *spiderSprite; // 蜘蛛对象中有一个精灵对象 9 int numUpdates;10 }11 12 +(id)spiderWithParentNode:(CCNode *)parentNode;13 -(id)initWithParentNode:(CCNode *)parentNode;14 @end

 

1 #import "Spider.h" 2 #import "MultiLayerScene.h" 3  4 @implementation Spider 5  6 #pragma mark 一个像 CCNode类一样的静态自动释放的初始化方法。这是在模仿 cocos2d 的内存管理方式。 7 +(id)spiderWithParentNode:(CCNode *)parentNode 8 { 9     return [[[self alloc] initWithParentNode:parentNode] autorelease];10 }11 12 -(id)initWithParentNode:(CCNode *)parentNode13 {14     if((self = [super init]))15     {16         CGSize screenSize = [[CCDirector sharedDirector] winSize];17         spiderSprite = [CCSprite spriteWithFile:@"spider.png"];18         spiderSprite.position = CGPointMake(CCRANDOM_0_1()*screenSize.width,CCRANDOM_0_1()*screenSize.height);19         [parentNode addChild:spiderSprite]; // 将预定好随机位置的蜘蛛加入到制定的节点对象(CCNode *)中20         21         // 自定义使用CCSchedule类手动预约更新方法22         [[[CCDirector sharedDirector] scheduler] scheduleUpdateForTarget:self23                                                                 priority:024                                                                   paused:NO];25         // 让这个类可以接收定向的触摸事件26         [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self27                                                                   priority:-1 swallowsTouches:YES];28     }29     return self;30 }31 32 -(void)update:(ccTime)delta33 {34     numUpdates ++;35     /*if(numUpdates > 50)36     {37         numUpdates = 0;38         [spiderSprite stopAllActions];39         CGPoint moveTo = CGPointMake(CCRANDOM_0_1()*200-100,CCRANDOM_0_1()*100 - 50);40         CCMoveBy *move = [CCMoveBy actionWithDuration:1 position:moveTo];41         [spiderSprite runAction:move];42     }*/43     if(numUpdates > 50)44     {45         numUpdates = 0;46         CGPoint moveTo = CGPointMake(CCRANDOM_0_1()*200-100,CCRANDOM_0_1()*100 - 50);47         [self moveAway:2 postion:moveTo];48     }49 }50 51 -(void)moveAway:(float)duration postion:(CGPoint)moveTo52 {53     [spiderSprite stopAllActions];54     CCMoveBy *move = [CCMoveBy actionWithDuration:duration position:moveTo];55     [spiderSprite runAction:move];56 }57 58 - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event59 {60     // CCLOG(@"Spride ccTouchBegan");61     CGPoint touchLocaion = [MultiLayerScene locationFromTouch:touch];62     BOOL isTouchHandled = CGRectContainsPoint([spiderSprite boundingBox], touchLocaion);63     if(isTouchHandled)64     {65         numUpdates = 0;66         CGPoint moveTo;67         float moveDistance = 60;68         float rand = CCRANDOM_0_1();69         if(rand < 0.25f)70         {71             moveTo = CGPointMake(moveDistance, moveDistance);72         }73         else if (rand < 0.5f)74         {75             moveTo = CGPointMake(-moveDistance, moveDistance);76         }77         else if (rand <0.75f)78         {79             moveTo = CGPointMake(moveDistance, -moveDistance);80         }81         else82         {83             moveTo = CGPointMake(-moveDistance, -moveDistance);84         }85         [self moveAway:0.1f postion:moveTo];86     }87     return isTouchHandled;88 }89 90 -(void)dealloc91 {92     // scheduler要求在dealloc方法中手动解除预约的更新方法93     [[[CCDirector sharedDirector] scheduler] unscheduleUpdateForTarget:self];94     95     // CCTouchDispatcher(触摸调度程序) 也需要在dealloc方法中被移除。96     [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];97     [super dealloc];98 }

 

 

转载于:https://www.cnblogs.com/sell/archive/2013/01/11/2856649.html

转载地址:https://blog.csdn.net/weixin_33819479/article/details/94547175 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:视频: 英语口音纠正课程
下一篇:cp2102通过GPIO连接树莓派

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月26日 00时25分11秒