自制游戏关卡编辑器的教程_第1页
自制游戏关卡编辑器的教程_第2页
自制游戏关卡编辑器的教程_第3页
自制游戏关卡编辑器的教程_第4页
自制游戏关卡编辑器的教程_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

分享开发者自制游戏关卡编辑器的教程这是关于如何创造《Cutthe》游戏的关卡编辑器教程系列文章的第第部分)在之前的教程中,你设计了关卡文件的存储机制,并执行了一些模型类别去控制绳索和菠萝的数据,精品文档放心下载最后还创造了关卡编辑器的加载文件功能。感谢阅读XML可以肯定的是你将不会再手动编辑这些关卡文件。在这系列教程的第二部分,你将执行关卡编辑器的编辑精品文档放心下载功能的某些部分。你将添加弹出菜单,在屏幕上动态地放置并调整目标对象的尺寸等等。谢谢阅读如果你未拥有之前的内容,你可以下载在第一部分教程中所创造的样本项目。感谢阅读开始所以关卡编辑器应该包含哪些功能?至少它必须能在关卡中创造,重置并删除对象。同时它还需要能够有谢谢阅读感谢阅读有效设置了这些基本要求后,你便可以开始设计编辑器了。感谢阅读首先,用户需要有一种方法去打开编辑器。最简单的方法便是使用菜单。屏幕最下方便是设置菜单的最佳谢谢阅读位置,并且不能包含任何动态游戏元素,如下:iOS-Simulator(fromraywenderlich)精品文档放心下载其次,用户必须能够在关卡上添加绳子和菠萝。这便意味着你需要添加一种机制让他们创造这些全新的对感谢阅读象。再次,最简单的方法仍是使用菜单去呈现这些功能。而因为你在屏幕最下方已经拥有一个菜单了,所以你可以快速运行基板面。谢谢阅读该做什么?比起拥有一个始终呈现的菜单,你最好设置一个轻敲屏幕后出现的弹出菜单。用户可以在这个弹出菜单上感谢阅读选择添加菠萝或绳索。然后在弹出菜单当前的位置上便会自动生成对象。谢谢阅读对于用户来说移动对象非常重要—-谢谢阅读放是一种逻辑选择。最后但同样重要的是,用户必须能够删除对象。在iOS上一个非常常见的方法便是使用长按行动去表示用谢谢阅读户想要删除那个被按住的对象。现在我们便决定了编辑器的设计,你可以开始构建它了!创造LevelEditor类在LevelEditor群组中创造一个Objective-C类。将文件命名为LevelEditor,将其设置为超级类CCLayer,精品文档放心下载并确保将文件的扩展名由.m改为.mm。.mm文件扩展名告诉编辑器文件使用的是Objective-C++。谢谢阅读为什么你需要在LevelEditor类中使用Objective-C++?谢谢阅读简单地来说,Box2D使用C++。同时,LevelEditor参考了其它依赖于Box2D的游戏类。感谢阅读让我们如下放置内容:#import“cocos2d.h”#import“”感谢阅读#import“”感谢阅读@interfaceLevelEditor:感谢阅读+(CCScene*)sceneWithFileHandler:(LevelFileHandler*)fileHandler;精品文档放心下载@end这添加了协议,让新LevelEditor层面能够接收碰触事件。感谢阅读接下来转换到并用如下代码替换里面的内容:感谢阅读#import“”#import“”精品文档放心下载#import“PineappleModel.h”谢谢阅读#import“RopeModel.h”#import“”谢谢阅读@interfaceLevelEditor(){感谢阅读LevelFileHandler*fileHandler;谢谢阅读background;pineapplesSpriteSheet;精品文档放心下载谢谢阅读}@end@implementationLevelEditor精品文档放心下载@end上述代码在LevelEditorfileHandler将储存你现在谢谢阅读正编辑的关卡,而背景是一个呈现丛林背景的精灵。对于所有菠萝和绳索还有两个。谢谢阅读现在添加sceneWithFileHandler:执行,如下:谢谢阅读+(CCScene*)sceneWithFileHandler:(LevelFileHandler*)fileHandler{感谢阅读CCScene*scene=[CCScenenode];感谢阅读LevelEditor*layer=[[LevelEditoralloc]initWithFileHandler:fileHandler];感谢阅读[sceneaddChild:layer];returnscene;}这一代码与Cocos2D项目模版的场景创造类似。它只创造了一个包含LevelEditor场景的CCScene。谢谢阅读这时候的场景还很空。所以让我们开始往场景中添加编辑器菜单。精品文档放心下载添加编辑器菜单在的任何位置上添加如下代码:感谢阅读-(void)createMenu{CGSizewinSize=[CCDirectorsharedDirector].winSize;精品文档放心下载//PlaceButtonsatbottomofgame谢谢阅读CCLabelTTF*saveLabel=[CCLabelTTFfontName:@"MarkerFelt"精品文档放心下载fontSize:24];CCMenuItem*saveItem=[CCMenuItemLabelitemWithLabel:saveLabeltarget:self感谢阅读selector:@selector(save)];感谢阅读CCLabelTTF*resetLabel=[CCLabelTTFfontName:@"MarkerFelt"感谢阅读fontSize:24];CCMenuItem*resetItem=[CCMenuItemLabelitemWithLabel:resetLabeltarget:self精品文档放心下载selector:@selector(resetLevel)];谢谢阅读CCLabelTTF*playLabel=[CCLabelTTFlabelWithString:@"PlayLevel"fontName:@"MarkerFelt"感谢阅读fontSize:24];CCMenuItem*playLevelItem=[CCMenuItemLabelitemWithLabel:playLabeltarget:self感谢阅读selector:@selector(playLevel)];精品文档放心下载//Createmenubuttons谢谢阅读CCMenu*menu=[CCMenumenuWithItems:saveItem,resetItem,playLevelItem,nil];感谢阅读[menualignItemsHorizontallyWithPadding:winSize.width*0.1f];精品文档放心下载menu.position=CGPointMake(winSize.width/2,saveItem.contentSize.height/2);谢谢阅读[selfaddChild:menuz:100];精品文档放心下载}-(void)save{//TODO:savelevel}-(void)resetLevel{//TODO:resettolastsavedversionofcurrentlyopenedfile谢谢阅读}-(void)playLevel{[[CCDirectorsharedDirector]replaceScene:[HelloWorldLayersceneWithFileHandler:fileHandler]];感谢阅读}感谢阅读“游戏关卡”三种标签。然后你创造了CCMenu并在此添加了菜单项目。最后你将CCMenu添加到场景精品文档放心下载中,从而让我们能够看到它。感谢阅读上的选择器。这时候只执行了playLevel—-轻敲这一菜单项目将转换到游戏场景中。你将在之后回来执行谢谢阅读其它两个方法。现在你需要一些代码去调用菜单并在屏幕上绘制出背景。在添加如下代码:-(id)initWithFileHandler:(LevelFileHandler*)levelFileHandler{谢谢阅读self=[superinit];if(self){fileHandler=levelFileHandler;精品文档放心下载[selfcreateMenu];background=spriteWithSpriteFrameName:@"bg.png"];谢谢阅读CGSizewinSize=[CCDirectorsharedDirector].winSize;精品文档放心下载background.position=CGPointMake(winSize.width/2,winSize.height/2);谢谢阅读background.color=kDefaultBackgroundColor;精品文档放心下载[selfaddChild:background];感谢阅读//Loadthespritesheetintothespritecache谢谢阅读sharedSpriteFrameCache]addSpriteFramesWithFile:@””];精品文档放心下载pineapplesSpriteSheet=batchNodeWithFile:@"CutTheVerlet.pvr.ccz"];谢谢阅读[selfaddChild:pineapplesSpriteSheet];感谢阅读=batchNodeWithFile:@"rope_texture.png"];精品文档放心下载[selfaddChild:ropeSpriteSheet];精品文档放心下载[selfdrawLoadedLevel];}returnself;}-(void)drawLoadedLevel{//drawpineapples//TODO:drawropes}上述代码初始化了关卡编辑器的屏幕。然后通过复制LevelFileHandler储存了关卡布局。接下来它调用了精品文档放心下载精品文档放心下载然后代码还初始化了背景,从而让它比最初的背景更暗。这将避免让用户混淆自己是在游戏模式中还是在谢谢阅读编辑模式中。这一绘画代码很棒,但是现在我们还不能让用户切换到关卡编辑器中!感谢阅读作为一种实践,让我们尝试着创造运行用户切换到关卡编辑器的菜单。提示:你可以通过遵循与之前提到感谢阅读的相同的菜单创造步骤去创造新菜单。所以试试看吧!创建并运行你的项目!当游戏正在运行时,碰触菜单切换到编辑器。你将看到丛林背景和底部的菜单。谢谢阅读选择“游戏关卡”选择将带你回到游戏中,如下:SwitchingWithMenu(fromraywenderlich)精品文档放心下载使用菜单来回切换在屏幕上绘制绳索现在你需要执行代码在屏幕上绘制关卡元素。菠萝较为简单。但是绳索却优点困难。感谢阅读你可以先处理较为复杂的问题,所以让我们先从绘制绳索开始!感谢阅读绳索可以拥有各种长度和方向。最简单的方法便是使用一个精灵和比例,然后相应地旋转。但是这么做所谢谢阅读创造出的绳索可能会很丑,如下:ScaledRope(fromraywenderlich)谢谢阅读朝任何方向旋转并拉升绳索精灵看起来很丑。你喜欢绳索的规格能够始终保持一样。最简单的方法便是使用被系住的较小绳段图像去创造任何理想长度谢谢阅读的绳索。为了整合这一绘制代码,我们需要在LevelEditor组中创造一个全新的Objective-C类。将其命名为谢谢阅读并将其设置为子类NSObject。感谢阅读切换到并用如下代码替换里面的内容:精品文档放心下载#import“cocos2d.h”#import“Constants.h”#import“RopeModel.h”@interface:NSObject精品文档放心下载@propertygetter=getID)intid;谢谢阅读-(id)initWithParent:(CCNode*)parentandRopeModel:(RopeModel*)aRopeModel;谢谢阅读-(int)getID;-(CGPoint)getAnchorA;-(CGPoint)getAnchorB;-(int)getBodyAID;-(int)getBodyBID;-(void)setAnchorA:(CGPoint)anchorA;精品文档放心下载-(void)setAnchorB:(CGPoint)anchorB;感谢阅读@end这定义了一些必要的方法和属性。大多数方法都是简单的getter和setter。谢谢阅读你可能会问:“为什么我们不能使用属性?”属性是执行getter和setter的简单方法;但是在这种情况下精品文档放心下载你会想要添加定制代码到setter上从而让它能够在属性改变时重新绘制绳索。感谢阅读注:你可能已经使用了属性并在执行中覆盖了getter和setter。但是在这个教程中的getter和setter方法感谢阅读能够更清楚地解释每个执行步骤。切换到并用以下代码替换其中的内容:精品文档放心下载#import“”#import“”谢谢阅读@interface(){感谢阅读RopeModel*ropeModel;}@end@implementation谢谢阅读@end上述代码在RopeModel,精品文档放心下载它将提供给你所有有关绳索的放置信息。现在在上添加以下代码:-(id)initWithParent:(CCNode*)parentandRopeModel:(RopeModel*)aRopeModel{精品文档放心下载self=[superinit];if(self){ropeModel=aRopeModel;=spriteWithFile:@"rope_texture.png"];感谢阅读=感谢阅读[ropeSprite.texture谢谢阅读[selfupdateRope];addChild:ropeSprite];精品文档放心下载}returnself;}上述方法包含了两个参数。父参数是关于绘制绳索的节点。这一参数后遵循着一个模式,即包含了绘制绳精品文档放心下载索所需要的所有信息。随后该代码还在一个实例变量中储存了绳索模式。谢谢阅读接下来,代码从文件“rope_texture.png”中创造了一个。该图像文件只包含绳索的一小段。随精品文档放心下载后你可以通过重复这一精灵而绘制出完整的绳索。你可以多次绘制同样的精灵去完成同样的内容,但这需要更多的代码。并且这样能够帮助你更好地设置绳感谢阅读索纹理去处理绘制任务。OpenGL中的问题具有一些你可能不熟悉的参数。结构包含以下领域:谢谢阅读(fromraywenderlich)精品文档放心下载OpenGL纹理参数minFiltermagFilter。感谢阅读关卡编辑器会使用GL_LINEAR去处理minFilter和magFilterGL_LINEAR感谢阅读带有纹理的像素中心)的加权平均数。换句话说,基于这一设置OpenGl我们可以使用线性插值去估算像感谢阅读素值。wrapS和wrapT参数让你能够在s和t坐标设置纹理的包装行为。谢谢阅读OpenGLs和tOpenGLy和z坐标是用于定义谢谢阅读x和y感谢阅读了s和t字母表示。你希望绳索能够沿着st谢谢阅读地进行呈现。对此你便可以使用GL_CLAMP_TO_EDGE值。精品文档放心下载现在你拥有带有纹理的,它能够沿着一条轴线不断重复并在其它轴上保持不变。多么整洁!感谢阅读而现在你唯一需要做的便是在屏幕上适当地呈现绳索去更新其长度和旋转。这一切都发生在updateRope谢谢阅读中,你将在下方执行。如下在中添加updateRope执行:感谢阅读-(void)updateRope{CGPointanchorA=[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorA];精品文档放心下载CGPointanchorB=[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorB];感谢阅读floatdistance=ccpDistance(anchorA,anchorB);谢谢阅读CGPoint=ccpSub(ccp(anchorA.x,anchorA.y),ccp(anchorB.x,anchorB.y));感谢阅读floatangle=精品文档放心下载ropeSprite.textureRect=CGRectMake(0,0,distance,ropeSprite.texture.pixelsHigh);谢谢阅读=ccpMidpoint(anchorA,anchorB);谢谢阅读=-1*CC_RADIANS_TO_DEGREES(angle);谢谢阅读}上述代码使用了ccpDistance精品文档放心下载精品文档放心下载然后,代码使用上述估算的绳索长度而改变了绳索的纹理。并更新了绳索的位置。你需要记住另外一个定谢谢阅读位点是在的中间,所以它的位置是在两个定位点之间。感谢阅读最后,代码设置了绳索精灵的角度。因为现在的角度是弧度,所以你需要使用感谢阅读CC_RADIANS_TO_DEGREES将其变成角度符号。谢谢阅读这便是关于你如何在未使用纹理的前提下绘制出一个任意长度的绳索。尽管比起简单的伸缩这要求更多的感谢阅读代码,但是却看起来更棒。作为额外的奖励,你可能已经借此掌握了OpenGL,并能够将其用于其它项目精品文档放心下载中了!现在你便完成了类的设置。只剩下添加getter和setter调用了。谢谢阅读所以你需要在上添加如下代码:感谢阅读-(void)setAnchorA:(CGPoint)theAnchorA{谢谢阅读ropeModel.anchorA=[CoordinateHelperscreenPositionToLevelPosition:theAnchorA];精品文档放心下载[selfupdateRope];}-(void)setAnchorB:(CGPoint)theAnchorB{感谢阅读ropeModel.anchorB=[CoordinateHelperscreenPositionToLevelPosition:theAnchorB];谢谢阅读[selfupdateRope];}-(int)getID{returnropeModel.id;}-(CGPoint)getAnchorA{return[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorA];感谢阅读}-(CGPoint)getAnchorB{return[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorB];谢谢阅读}-(int)getBodyAID{returnropeModel.bodyAID;}-(int)getBodyBID{returnropeModel.bodyBID;}当其中一个绳索的定位点发生改变,getter和setter只是简单的调用updateRope便可。这将重新绘制绳感谢阅读索去反应改变。创建并运行你的应用!等等,现在你看到的仍像之前的屏幕那样空旷,不是吗?谢谢阅读iOS-Simulator(fromraywenderlich)感谢阅读为什么不能看到新添加的内容?虽然你已经执行了类,但是你并未使用它在屏幕上绘制任何内容。谢谢阅读在屏幕上绘制游戏对象在将这么多内容整合到绘制方法后,你便已经能够开始绘制关卡了。谢谢阅读你需要将一个新变量添加到中,这是储存了所有关卡中的绳索精灵的数组。感谢阅读首先在中添加如下输入内容:精品文档放心下载#import“”现在在最上方的类扩展中(@interface组块)添加新绳索数组的声明:感谢阅读NSMutableArray*ropes;好了,现在你便能够在屏幕上绘制当前的关卡了。是时候整合所有内容并检测结果了!谢谢阅读在的drawLoadedlevel中添加如下代码,并替换现有的行:谢谢阅读//Drawpineapplefor(PineappleModel*pineapplein{精品文档放心下载[selfcreatePineappleSpriteFromModel:pineapple];感谢阅读}//Drawropesropes=[NSMutableArrayarrayWithCapacity:5];精品文档放心下载for(RopeModel*ropeModelin{感谢阅读[selfcreateRopeSpriteFromModel:ropeModel];精品文档放心下载}上述代码只迭代了fileHandler感谢阅读现。现在通过添加方法到而执行方法去创造菠萝精灵:谢谢阅读-(void)createPineappleSpriteFromModel:(PineappleModel*)pineappleModel{精品文档放心下载=spriteWithSpriteFrameName:@"pineapple.png"];谢谢阅读=pineappleModel.id;谢谢阅读CGPointposition=[CoordinateHelperlevelPositionToScreenPosition:pineappleModel.position];谢谢阅读pineappleSprite.position=position;感谢阅读[pineapplesSpriteSheetaddChild:pineappleSprite];感谢阅读}上述方法创造了一个包含菠萝图像的精灵。然后从pineappleModel变量中检索了菠萝的ID和位置,并将谢谢阅读其相应地分配到中。最后,它添加了菠萝精灵到中。感谢阅读创造绳索精灵的方法也是遵循着这一逻辑。在下方添加如下代码:精品文档放心下载-(void)createRopeSpriteFromModel:(RopeModel*)ropeModel{精品文档放心下载CGPointanchorA;if(ropeModel.bodyAID==-1){谢谢阅读anchorA=[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorA];谢谢阅读}else{PineappleModel*pineappleWithID=[fileHandlergetPineappleWithID:ropeModel.bodyAID];精品文档放心下载anchorA=[CoordinateHelperlevelPositionToScreenPosition:pineappleWithID.position];感谢阅读}CGPointanchorB;if(ropeModel.bodyBID==-1){谢谢阅读anchorB=[CoordinateHelperlevelPositionToScreenPosition:ropeModel.anchorB];精品文档放心下载}else{PineappleModel*pineappleWithID=[fileHandlergetPineappleWithID:ropeModel.bodyBID];谢谢阅读anchorB=[CoordinateHelperlevelPositionToScreenPosition:pineappleWithID.position];感谢阅读}=alloc]initWithParent:ropeSpriteSheetandRopeModel:ropeModel];精品文档放心下载[ropesaddObject:ropeSprite];感谢阅读}该方法首先明确了绳索的定位点。如果bodyID是-1的话,它便会将anchorPosition值储存在ropeModel谢谢阅读中。否则它会根据特定的bodyID去使用菠萝的位置。然后它使用了信息去创造一个实例并将谢谢阅读其添加到ropes数组中。创建并运行你的游戏,然后切换到关卡编辑器中。现在你便能够看到屏幕上自己所创造的图像了,如下截精品文档放心下载图所示:Screen-Shot(fromraywenderlich)精品文档放心下载检测用户输入:碰触,移动和长按看到屏幕上的内容已经很棒了,但是你还需要一些其它行动!现在你不能只是做一些关卡编辑。你需要让精品文档放心下载关卡编辑器去处理用户输入。你需要在编辑器上处理的一些用户互动只是一些场景的碰触,如拖放和长按。感谢阅读首先,在上添加一个实例变量去储存能够识别长按的手势识别器:感谢阅读UILongPressGestureRecognizer*longPressRecognizer;感谢阅读现在在上添加如下代码:感谢阅读-(void)onEnter{[superonEnter];[[CCDirectorsharedDirector].touchDispatcherpriority:0感谢阅读longPressRecognizer=[[UILongPressGestureRecognizeralloc]精品文档放心下载action:@selector(longPress:)];谢谢阅读=[CCDirectorsharedDirector].view;感谢阅读addGestureRecognizer:longPressRecognizer];感谢阅读}当视图切换到LevelEditoronEnterLevelEditor实例作为碰触处理者,谢谢阅读从而它将收到像和等输入方法的调用。同时创造longPressRecognizer并精品文档放心下载将其添加到作为一个手势识别器。感谢阅读因为关卡编辑器是代表碰触事件,所以你需要添加能够在之后处理碰触输入的相关代表方法。谢谢阅读在中添加如下代码:-(void)longPress:(UILongPressGestureRecognizer*)longPressGestureRecognizer{谢谢阅读if(longPressGestureRecognizer.state==UIGestureRecognizerStateBegan){精品文档放心下载NSLog(@”longpressbegan”);谢谢阅读}}*)touchwithEvent:(UIEvent*)event{谢谢阅读NSLog(@”touchbegan”);returnYES;}*)touchwithEvent:(UIEvent*)event{感谢阅读NSLog(@”touchmoved”);}*)touchwithEvent:(UIEvent*)event{谢谢阅读NSLog(@”touchended”);}longPress首先检查了手势识别器当前的状态。手势识别器可以拥有其中一个不同的状态。但是你只有知感谢阅读UIGestureRecognizerStateBegan感谢阅读的记录信息。这时候你还漏掉一件事:在用户离开关卡编辑器后进行清理。精品文档放心下载在上添加如下代码:-(void)onExit{[superonExit];[[CCDirectorsharedDirector].touchDispatcherremoveDelegate:self];谢谢阅读=[CCDirectorsharedDirector].view;感谢阅读removeGestureRecognizer:longPressRecognizer];感谢阅读}上述代码删除了作为碰触调度程序的碰触以及手势识别器。感谢阅读Xcode精品文档放心下载敲,拖放或长按屏幕所产生的信息:iOS-Simulator(fromraywenderlich)感谢阅读添加弹出编辑器菜单看到所有的这些行动真的很棒。但是单凭记录信息并不能创造一个编辑器!现在你需要让用户能够在屏幕精品文档放心下载上与对象进行互动。你需要解决的第一个问题便是如何添加新对象。你已经决定了需要保守设置屏幕空间。这也是为何你不能感谢阅读在屏幕上添加另一个菜单去添加新道具,相反地,编辑器能够打开一个弹出菜单,让用户选择是添加绳索谢谢阅读还是菠萝。当玩家轻敲菜单时你希望弹出菜单能够出现让他们再次做出选择。如下:感谢阅读popupmenu-default(fromraywenderlich)感谢阅读使用CCLayer创造一个名为PopupMenu的全新Objective-C类,作为超级类。谢谢阅读现在切换到PopupMenu.h并用如下代码替换其中的内容:感谢阅读#import“cocos2d.h”@protocolPopupMenuDelegate感谢阅读-(void)createPineappleAt:(CGPoint)position;精品文档放心下载-(void)createRopeAt:(CGPoint)position;谢谢阅读@end@interfacePopupMenu:CCLayer谢谢阅读@propertyid<PopupMenuDelegate>delegate;谢谢阅读-(id)initWithParent:(CCNode*)parent;感谢阅读-(void)setPopupPosition:(CGPoint)position;感谢阅读-(void)setMenuEnabled:(BOOL)enable;精品文档放心下载-(void)setRopeItemEnabled:(BOOL)enabled;精品文档放心下载-(BOOL)isEnabled;@end上述代码提出了一个新协议。该协议定义了PopupMenu和任何其它类(游戏邦注:会在用户选择菜单中精品文档放心下载一个条目时得到通知)之间的界面。该协议定义了两个方法,createPineappleAt:和createRopeAt:,他们将在各自对象实例被创造出来时进行谢谢阅读调用。PopupMenu类定义在PopupMenuDelegate谢谢阅读单中做出选择时被调用。打开PopupMenu.m并用以下代码替换其中的内容:谢谢阅读#import“PopupMenu.h”#import“”@interfacePopupMenu(){background;CCMenu*menu;CCMenuItem*ropeItem;CGPointtapPosition;BOOLisEnabled;}@end@implementationPopupMenu@end与之前一样,这是一个有关输入和私有变量的架构。变量往背景和菜单中添加了参考。此外,这里还有一感谢阅读个关于绳索菜单项的指示器,让你可以改变菜单项的状态。因为你只能在有菠萝可以捆绑的前提下才能创感谢阅读造绳索。这里有一个tapPosition谢谢阅读isEnabled指示着玩家是否能够看到现在屏幕上的弹出菜单。谢谢阅读现在在PopupMenu.m上添加如下代码:-(id)initWithParent:(CCNode*){谢谢阅读self=[superinit];if(self){=spriteWithFile:@"pineappleitem.png"];精品文档放心下载pineappleSpriteSelected=spriteWithFile:@"pineappleitem.png"];感谢阅读pineappleSpriteSelected.color=ccc3(100,0,0);感谢阅读CCMenuItemImage*pineappleItem=[CCMenuItemImage谢谢阅读target:selfselector:@selector(createPineapple:)];感谢阅读=spriteWithFile:@"ropeitem.png"];精品文档放心下载=spriteWithFile:@"ropeitem.png"];感谢阅读=spriteWithFile:@"ropeitem.png"];感谢阅读ropeSpriteSelected.color=ccc3(100,0,0);谢谢阅读ropeSprite3.color=ccc3(100,100,100);谢谢阅读ropeItem=[CCMenuItemImage精品文档放心下载target:selfselector:@selector(createRope:)];精品文档放心下载menu=[CCMenumenuWithItems:pineappleItem,ropeItem,nil];感谢阅读background=spriteWithFile:@"menu.png"];精品文档放心下载[backgroundaddChild:menuz:150];感谢阅读[selfaddChild:background];感谢阅读addChild:selfz:1000];精品文档放心下载[selfsetMenuEnabled:NO];}returnself;}这一新方法将CCNode谢谢阅读一些和一个CCMenu并将其添加到源节点上。这一方法也关闭了菜单的使用,因为它只能在用谢谢阅读户提出要求时使用。下图是创造弹出菜单的组成部分:structure-popup-menu(fromraywenderlich)精品文档放心下载感谢阅读一个是关于菠萝,另一个是关于绳索。菜单的位置在PopupMenu.m上添加如下代码去设置菜单的准确位置:谢谢阅读-(void)setPopupPosition:(CGPoint)position{精品文档放心下载tapPosition=position;//loaddefaultBackgroundandusesizetodeterminewhetherthepopupstillthere精品文档放心下载defaultBackground=spriteWithFile:@"menu.png"];精品文档放心下载CGSizedefaultBackgroundSize=defaultBackground.contentSize;精品文档放心下载floatcontentScaleFactor=[CCDirectorsharedDirector].contentScaleFactor;谢谢阅读floatpadding=defaultBackgroundSize.width*0.1f*contentScaleFactor;精品文档放心下载[menualignItemsHorizontallyWithPadding:padding];精品文档放心下载CGPointanchorPoint=CGPointMake(0.5f,0.0f);谢谢阅读CGPointmenuPosition=CGPointMake(defaultBackgroundSize.width/2,谢谢阅读defaultBackgroundSize.height*0.7f);谢谢阅读//TODO:adjustanchorPointandorientationofmenu,tomakeitfitthescreen感谢阅读background.anchorPoint=anchorPoint;谢谢阅读background.position=position;谢谢阅读background.opacity=menu.opacity;谢谢阅读menu.position=menuPosition;谢谢阅读}为了有效地为菜单定位,上述代码先加载了菜单的背景精灵然后明确了它的大小。该数值能够用于计算菠谢谢阅读萝和绳索菜单项之间该如何填充。然后方法从CCDirector中请求了contentScaleFactor。contentScaleFactor能够将像素位置换成点位置。谢谢阅读在iOS谢谢阅读但是基于Cocos2D,菜单中两个项目间的填充从某种原因上来看仍是鉴于像素。因此你需要使用精品文档放心下载contentScaleFactor将填充从点转换成像素。谢谢阅读接下来,anchorPoint和menuPosition被设置为默认值。定位点也被设置成箭头(在图像的中下方)的尖谢谢阅读端。xy感谢阅读一的高度。将菜单放置在背景图像三分之二位置便能够最准确地出现在玩家面前。谢谢阅读到现在为止一切设置似乎都很顺利。除了一些来自Xcode的未生效方法的糟糕指示。精品文档放心下载所以现在你需要添加那些遗漏掉的方法。在PopupMenu.m添加如下方法:-(BOOL)isEnabled{returnisEnabled;}-(void)setMenuEnabled:(BOOL)enable{谢谢阅读for(CCMenuItem*iteminmenu.children){精品文档放心下载item.isEnabled=enable;}isEnabled=enable;intopacity;if(enable){=}else{=}background.opacity=精品文档放心下载menu.opacity=}-(void)setRopeItemEnabled:(BOOL)enabled{感谢阅读ropeItem.isEnabled=enabled;谢谢阅读}-(void)createPineapple:(id)sender{谢谢阅读[self.delegatecreatePineappleAt:tapPosition];精品文档放心下载}-(void)createRope:(id)sender{精品文档放心下载[self.delegatecreateRopeAt:tapPosition];精品文档放心下载}上述的方法非常直接。setMenuEnabled:,顾名思义就是让你打开或关闭菜单。该方法将所有菜单项设置谢谢阅读为适当的状态,然后调整菜单的不透明度,意味着完全可见,则意味着不可见。感谢阅读setRopeltemEnabled谢谢阅读重要,它能够防止用户创造出无效的关卡。当你轻敲菠萝或绳索的菜单项时便能够调用最后两个方法。它们都是代表的标志。感谢阅读是使用运行菜单了。切换到并添加如下输入:感谢阅读#import“PopupMenu.h”接下来将PopupMenuDelegate添加到@interface行,如下:谢谢阅读@interfaceLevelEditor:PopupMenuDelegate>谢谢阅读现在你的LevelEditor类正在执行PopupMenuDelegate。这便意味着它能够听从当前弹出菜单的顺序了。感谢阅读切换到并在最上方的@interface添加弹出菜单的实例变量:精品文档放心下载PopupMenu*popupMenu;现在在中执行弹出菜单代表方法:谢谢阅读-(void)createPineappleAt:(CGPoint)position{感谢阅读NSLog(@”createpineapple”);精品文档放心下载[popupMenusetMenuEnabled:NO];感谢阅读}-(void)createRopeAt:(CGPoint)position{感谢阅读NSLog(@”createrope”);[popupMenusetMenuEnabled:NO];精品文档放心下载}现在的代表方法只能暂时记录它们被调用的状态,并关闭弹出菜单。精品文档放心下载如果你现在想要创建并运行代码,那么弹出菜单便不可能出现,因为不存在任何内容能够打开菜单。精品文档放心下载你需要在添加如下代码:谢谢阅读-(void)createPineappleAt:(CGPoint)position{精品文档放心下载NSLog(@”createpineapple”);谢谢阅读[popupMenusetMenuEnabled:NO];精品文档放心下载}-(void)createRopeAt:(CGPoint)position{感谢阅读NSLog(@”createrope”);[popupMenusetMenuEnabled:NO];谢谢阅读}上述代码能够切换菜单的状态。它首先明确了弹出菜单是否已经存在。如果菜单并不存在,它便会创造一精品文档放心下载个新实例并将LevelEditor记为代表。而如果现在菜单被启动了,你便可以关闭它。如果它被关闭了,你也可以开启它,并将其位置设为碰触位精品文档放心下载置。如果现在在关卡中并不存在菠萝,你便可以在弹出菜单中关闭绳索的使用。精品文档放心下载现在只剩下在玩家轻拍屏幕时调用togglePopupMenu:。你需要改变中的哪些方法?精品文档放心下载!将当前代码替换为:*)touchwithEvent:(UIEvent*)event{谢谢阅读CGPointtouchLocation=[touchlocationInView:touch.view];谢谢阅读touchLocation=[[CCDirectorsharedDirector]感谢阅读[selftogglePopupMenu:touchLocation];谢谢阅读returnYES;}前面两行计算了用户轻拍屏幕的位置。之后该位置用于呈现或隐藏弹出菜单。精品文档放心下载谢谢阅读popup-menu-cut-off-smaller(fromraywenderlich)感谢阅读你是怎么想的?你是否注意到当你轻拍屏幕上方或边缘时菜单的呈现有点奇怪?精品文档放心下载当你的碰触越接近屏幕边缘,菜单便会被截去更多内容。这看起来很奇怪。感谢阅读所以为了解决这一问题,你需要挑战背景图像及其定位点,从而让箭头的尖端能够直接指向轻拍屏幕的位感谢阅读置,而菜单也能够始终完整地呈现出来。执行动态弹出菜单定位比起使用复杂算式去计算弹出菜单的方向和位置,你可以将屏幕划分成一些区域。每个区域都拥有有关弹谢谢阅读出菜单的特有配置。下图便是一种可行的屏幕划分:Slide(fromraywenderlich)你必须根据菜单背景图像的大小去确定屏幕的区域划分。为什么?因为如果你是根据弹出菜单的大小进行谢谢阅读位置计算,之后你不仅可以切换到不同的弹出图像,同时也可以保留弹出菜单及其定位机制。谢谢阅读下图是关于在每个屏幕位置上定位菜单所需要的调整对象:精品文档放心下载popup-menu-positioning(fromraywenderlich)谢谢阅读首先你希望背景图像的箭头是指向碰触位置。你可以通过设置箭头的定位点而轻松做到这点。在默认方向感谢阅读上,箭头是指向中西方,也就是定位点为(0.5,0.0精品文档放心下载但是当弹出菜单位于右上方时,箭头就需要指向右上方。因此你需要将箭头的定位点调整为(,0.75感谢阅读你同样需要相对应地挑战菜单的位置。你需要注意到菜单是菜单背景的产物,所以你必须将其设置在背景坐标轴内。同时注意到菜单项的定线有精品文档放心下载时候需要从水平变成垂直。也许这听起来会让人感到却步,但是你将发现这是改变绘制代码的直接方法,所以它能够准确地为每个对感谢阅读象定位。切换到PopupMenu.m并在setPopupPosition:中找到行。感谢阅读用如下代码换掉行://Menuhorizontalalignment谢谢阅读CGSizewinSize=[CCDirectorsharedDirector].winSize;精品文档放心下载horizontalAlignment;谢谢阅读if(position.x<defaultBackgroundSize.width/2){谢谢阅读//horizontalAlignment=@””;谢谢阅读anchorPoint.x=0.0f;[menualignItemsVerticallyWithPadding:padding];精品文档放心下载menuPosition.x=defaultBackgroundSize.height*0.7f;谢谢阅读menuPosition.y=defaultBackgroundSize.width*0.5f;感谢阅读}elseif(winSize.width-position.x<defaultBackgroundSize.width/2){精品文档放心下载//righthorizontalAlignment=@”right”;精品文档放心下载anchorPoint.x=1.0f;[menualignItemsVerticallyWithPadding:padding];精品文档放心下载menuPosition.x=defaultBackgroundSize.height*0.3f;感谢阅读menuPosition.y=defaultBackgroundSize.width*0.5f;感谢阅读}else{//centerhorizontalAlignment=@”center”;精品文档放心下载[menualignItemsHorizontallyWithPadding:padding];精品文档放心下载}defaultBackgroundImagex左边小感谢阅读于背景图像宽度的一半,那么菜单将突向左边。因此你需要设置水平向右对齐并调整anchorPoint。感谢阅读你也可以采取同样的方法设置菜单定线为垂直,并在弹出菜单中调整菜单的位置。感谢阅读右边区域的设置也是如此,即只要沿着x轴进行设置。在其它例子中,水平定线被设定在中间位置,而菜精品文档放心下载单也是水平对齐。以下图像是关于每个可能方向的弹出菜单元素的坐标轴和定线:感谢阅读positioning-left-and-right(fromraywenderlich)谢谢阅读你之前添加的代码已经处理了弹出菜单的水平定线,但是垂直方向和相对应的定位点呢?这便是你接下来精品文档放心下载的工作!不要担心,以下内容是你在独自执行setPopupPosition中的代码的相关参考。精品文档放心下载你该怎么做?加上垂直定线代码,现在你便拥有弹出菜单的垂直和水平定线,并且你也已经储存了相一致感谢阅读的定位点。现在使用信息去完善弹出菜单的绘制吧。在setPopupPosition的水平和垂直定线检测代码后的PopupMenu.m添加如下代码:精品文档放心下载//Drawthemenufilename=stringWithFormat:@"menu-%@-%@.png",verticalAlignment,谢谢阅读horizontalAlignment];tex=addImage:filename];感谢阅读if{tex=addImage:@”menu.png”];精品文档放心下载}[background谢谢阅读[backgroundCGRectMake(0,0,tex.contentSize.width,tex.contentSize.height)];精品文档放心下载第一行创建了一串包含了背景图像(适合当前的方向)文件名的字符。每个方向的文件都是遵循谢谢阅读menu-verticalAlignment-horizontalAlignment.png模式进行命名。感谢阅读或者就是使用默认纹理。不要忘记背景图像的大小并不都是相同的。这也是你为何需要设置纹理结构去适应新纹理的一大原因。感谢阅读创建并运行你的应用,改变编辑器模式。在完成这些工作后,现在的此单已经能够根据屏幕调整方向了,精品文档放心下载如下:iOS-Simulator(fromraywenderlich)精品文档放心下载添加新的游戏对象—菠萝现在的弹出菜单已经能够有效运行了,你可以使用它在关卡上插入新的游戏对象。这听起来好像很接近最感谢阅读终产品了!让我们从菠萝开始设置,它们的执行比绳索更加简单。你需要仔细思考往关卡中添加新对象需要做些什么:1.创造能够代表游戏对象的模式生成一个独特的ID设置所有对象的参数2.在关卡文件处理程序中添加模式,这是代表关卡数据加载和储存谢谢阅读3.创造代表屏幕上模型的形象首先你需要一个独特的ID。如果你假设只有一种应用线程能够创造并删除对象,那么生成一个独特ID的感谢阅读相对简单的方法便是为游戏对象数组分类。当数组被分类后,你便需要在类别列表中迭代。你所遗漏的第一个索引便是第一个未使用过的ID。感谢阅读打开并添加如下方法为游戏对象数组分类:感谢阅读+(void)sortArrayById:(NSMutableArray*)objectArray{感谢阅读NSSortDescriptor*sortDescriptor=[[NSSortDescriptoralloc]initWithKey:@”id”ascending:YES];感谢阅读NSArray*sortDescriptors=[NSArrayarrayWithObject:sortDescriptor];感谢阅读[objectArraysortUsingDescriptors:sortDescriptors];精品文档放心下载}这看起来就像是简略版的分类算法。因为Objective-C已经执行了一种方便的分类算法,所以你可以无需感谢阅读重新创造了。你只需要明确分类算法的分类标准便可。NSSortDescriptor便可以帮你做到这点。在这一特殊例子中,你感谢阅读创造了一个非常简单的描述项,即以属性命名的“id”进行升序排序。精品文档放心下载然后你在数组中添加了这一分类描述,并将数组传达到sortUsingDescriptors:精品文档放心下载通过数组传达分类描述项看起来很麻烦,但是如果你想要基于更多属性进行分类的话,这一方法便能派上感谢阅读用场。在上添加如下方法:精品文档放心下载+(NSNumber*)firstUnusedIdInArray:(NSArray*)array{精品文档放心下载NSNumber*firstUnusedID=nil;谢谢阅读intlastID=0;for(AbstractModel*objectinarray){谢谢阅读if(object.id–lastID>1){谢谢阅读firstUnusedID=[NSNumbernumberWithInt:lastID+1];谢谢阅读break;}lastID++;}if(!firstUnusedID){firstUnusedID=[NSNumbernumberWithInt:lastID+1];精品文档放心下载}returnfirstUnusedID;}这一方法首先创造了firstUnusedID储存了第一个未用过的IDlastID则是用于储存代码所考感谢阅读虑的最后一个ID。然后迭代游戏对象数组中所包含的所有模式。谢谢阅读在每次迭代中你都检查了现在游戏对象的ID与最后一个(比之前ID感谢阅读话,你就需要找到一个没人使用过的ID,然后储存这个ID的值并退出循环。谢谢阅读你也可能在游戏对象数组中找不到一个未曾使用过的ID。在这种情况下,firstUnusedID将仍为nil。而你精品文档放心下载需要做的便是将firstUnusedID设置为lastID的值加。精品文档放心下载现在你可以使用来自上述方法的ID为新菠萝生成一个PineappleModel。感谢阅读在添加如下代码:精品文档放心下载-(PineappleModel*)addPineappleAt:(CGPoint)position{感谢阅读NSNumber*firstUnusedID=[LevelFileHandlerfirstUnusedIdInArray:self.pineapples];精品文档放心下载PineappleModel*newPineapple=[[PineappleModelalloc]init];精品文档放心下载newPineapple.id=[firstUnusedID精品文档放心下载newPineapple.position=position;精品文档放心下载[self.pineapplesaddObject:newPineapple];谢谢阅读returnnewPineapple;}这一代码是在执行之前所讨论的新游戏对象生成的点句。你可以抓取一个未被使用过的ID,创造一个新的PineappleModel实例,然后分配所有重要的参数,如ID谢谢阅读和位置。之后你可以添加一个新创造的菠萝到菠萝列表上。最后回到最新创造的菠萝中。感谢阅读因为你需要从LevelFileHandler类外部访问上述方法,所以它需要被公开。谢谢阅读在中添加如下方法:感谢阅读-(PineappleModel*)addPineappleAt:(CGPoint)position;感谢阅读现在你可以在用户需要时从关卡编辑器调用addPineappleAt:去创造全新菠萝。精品文档放心下载切换到并用下面代码替换现有的虚拟createPineappleAt:执行:感谢阅读-(void)createPineappleAt:(CGPoint)position{精品文档放心下载CGSizewinSize=[CCDirectorsharedDirector].winSize;谢谢阅读PineappleModel*pineappleModel=[fileHandleraddPineappleAt:CGPointMake(position.x/winSize.width,精品文档放心下载position.y/winSize.height)];谢谢阅读[selfcreatePineappleSpriteFromModel:pineappleModel];感谢阅读[popupMenusetMenuEnabled:NO];感谢阅读}PineappleModel精品文档放心下载之,PineappleModel能够用于创造一个,并在屏幕上呈现菠萝。最后,因为禁用了弹出精品文档放心下载菜单,所以你在这里便不会看到它。恭喜你!现在你已经执行了首次互动,即让用户可以修改关卡了。感谢阅读创建并运行你的应用,根据你的喜好往关卡里添加菠萝吧!精品文档放心下载iOS-Simulator(fromraywenderlich)感谢阅读添加新游戏对象——绳索往关卡中添加新绳索需要考虑更多。如果你只是让用户在任何地方添加绳索的话,那么大多数关卡将是无谢谢阅读效的,因为绳索需要连接到两个不同的主体。这些主体可以是两个不同的菠萝,或者一个菠萝和背景。感谢阅读你希望确保用户不会创造出任何无效的关卡,即创造出未能与两个有效主体维系在一起的绳索。感谢阅读做到这点的一种方法便是只接受菠萝作为第一个锚点。然后你可以接受任何其它对象作为第二个锚点(游感谢阅读感谢阅读点还是第二个?为此你可以使用一个状态机器。以下图表是关于该项目中所使用的状态机器:谢谢阅读State-Diagram-for-Adding-Rope(fromraywenderlich)感谢阅读为了执行这一机器,你需要种状态:1.kEditMode谢谢阅读会转换到第二种状态。2.kRopeAnchorPineappleMode,在此只能选择菠萝。当用户做出选择后,模式将转换到第三种状态。感谢阅读3.kRopeAnchorAnyMode,在此用户只能在任何菠萝中做出选择,除了第一个或背景上的菠萝。当用户做谢谢阅读出选择后,编辑器将转换会第一种模式。切换到并在输入后添加如下代码:精品文档放心下载enum{kEditMode,kRopeAnchorPineappleMode,kRopeAnchorAnyMode}typedefeditorMode;代码已经为上述状态创造了一个枚举去简化状态机器的执行。精品文档放心下载在的@interface组块中添加如下代码:精品文档放心下载editorModemode;RopeModel*newRope;在此你添加了一个储存了当前模式的实例变量,并伴随着一个变量将储存新绳索的参考。谢谢阅读尽管在代码中使用状态去组织用户创造出无效的关卡很有用,但是你也应该向用户指明当前的状态。通过精品文档放心下载从视觉上向用户指示状态转换,他们便能够很明显地看出自己正处于怎样的模式并能够执行怎样的行动。精品文档放心下载你可以通过在屏幕上添加颜色效果去指示当前的状态。红色代表项目不可选择,绿色代表项目可被选择。精品文档放心下载在中添加如下方法:-(void)setMode:(editorMode)newMode{感谢阅读mode=newMode;switch(mode){casekRopeAnchorPineappleMode:精品文档放心下载background.color=kDefaultDisabledColor;感谢阅读forpineapplein[pineapplesSpriteSheetchildren]){精品文档放心下载pineapple.color=kDefaultSelectableColor;感谢阅读}break;casekRopeAnchorAnyMode:background.color=kDefaultSelectableColor;谢谢阅读forpineapplein[pineapplesSpriteSheetchildren]){精品文档放心下载if(pineapple.tag==newRope.bodyAID){谢谢阅读pineapple.color=kDefaultDisabledColor;谢谢阅读}else{pineapple.color=kDefaultSelectableColor;感谢阅读}}break;casekEditMode:default:background.color=kDefaultBackgroundColor;感谢阅读forpineapplein[pineapplesSpriteSheetchildren]){谢谢阅读pineapple.color=ccc3(255,255,255);感谢阅读}break;}}switch谢谢阅读此假设kEditMode声明为默认的。对于每种状态,你使用了颜色属性去设置屏幕上的每个对象的颜色。精品文档放心下载在状态kRopeAnchorPineappleMode中,你将背景颜色设置为关闭,并打开所有菠萝。谢谢阅读在状态kRopeAnchorMode中,你改变了颜色所以背景便被激活了,同时还有所有菠萝—-除了带有谢谢阅读newRope首个锚点ID的菠萝。最后,在kEditMode中,将背景颜色以及所有菠萝颜色设为默认颜色。谢谢阅读是时候明确这一代码是否是你想看到的了!在中找到createRopeAt:,并用如下代码替换当前虚拟的执行内容:感谢阅读-(void)createRopeAt:(CGPoint)position{感谢阅读[selfsetMode:kRopeAnchorPineappleMode];感谢阅读newRope=[[RopeModelalloc]init];谢谢阅读[popupMenusetMenuEnabled:NO];感谢阅读}当用户决定创造一条新的绳索时,你设置模式为kRopeAnchorPineappleMode感谢阅读知用户只有这些对象能够作为绳索的第一个锚点。接下来,新绳索将被设置在一个空旷的模式中。最后,感谢阅读因为不再需要弹出菜单,所以你便关闭它。创建并运行你的项目,在弹出菜单中选择绳索。你将看到背景变成红色而菠萝都变成绿色,如下图:精品文档放心下载iOS-Simulator(fromraywenderlich)感谢阅读这看起来很整洁,但是你还不能真正添加新绳索!再次着眼于状态图表并明确你接下来该做些什么:感谢阅读State-Diagram-for-Adding-Rope(fromraywenderlich)谢谢阅读你需要察觉到玩家是否碰触了菠萝。为此你需要一个能在屏幕上设定位置的方法,并返回在该位置上包含感谢阅读菠萝的。在上添加如下代码:-(CCSprite*)pineappleAtPosition:(CGPoint)position{精品文档放心下载forpineapplein[pineapplesSpriteSheetchildren]){感谢阅读if(CGRectContainsPoint(pineapple.boundingBox,position)){谢谢阅读returnpineapple;}}returnnil;}CGRectContainsPoint去明确特定位置是否位于菠萝感谢阅读的boundingBox感谢阅读该方法将回到nil。现在你需要一个方法去执行首个定位点选择。在添加如下代码:-(void)selectFirstAnchor:(CGPoint)touchLocation{感谢阅读//ifuseronp

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论