UIAttachmentBehavior 不适用于可能会丢失的乒乓 AI
UIAttachmentBehavior not working for pong AI that can lose
所以我正在尝试实现一个基本的 pong AI,它很不错,但可以被打败。我试过 [UIView animateWithDuration...] UISnapBehavior 和 UIAttachmentBehavior。
animateWithDuration 没有正确更新碰撞检测,所以我得到了隐形墙
UISnapBehavior 还是太快了,不响应摩擦或阻力减速
UIAttachmentBehavior 似乎是我最好的选择,因为它适用于我的碰撞行为,如 Snap,但更灵活。我打算调整频率来影响速度。但是,我无法让它工作。欢迎所有建议,这里是代码:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[self startGame];
}
让一切顺利进行的基本方法
- (void)startGame {
[self createBall];
[self createPlayerPaddle];
[self createAIPaddle];
[self createCollisions];
// Remove rotation
self.paddleDynamicProperties = [[UIDynamicItemBehavior alloc] initWithItems:@[self.paddleView, self.paddleViewAI]];
self.paddleDynamicProperties.allowsRotation = NO;
//make heavy
self.paddleDynamicProperties.density = 1000.0f;
//make slow
self.paddleDynamicProperties.friction = 1000;
[self.animator addBehavior:self.paddleDynamicProperties];
}
我们将看看碰撞和 paddleViewAI
- (void)createCollisions {
self.collider = [[UICollisionBehavior alloc] initWithItems:@[self.ballView, self.paddleView, self.paddleViewAI]];
//self.collider.collisionDelegate = self.paddleView;
self.collider.collisionMode = UICollisionBehaviorModeEverything;
[self.collider addBoundaryWithIdentifier:@"left" fromPoint:CGPointMake(0, 0) toPoint:CGPointMake(0, self.view.frame.size.height)];
[self.collider addBoundaryWithIdentifier:@"right" fromPoint:CGPointMake(self.view.frame.size.width, 0) toPoint:CGPointMake(self.view.frame.size.width, self.view.frame.size.height)];
[self.animator addBehavior:self.collider];
}
- (void)createAIPaddle {
CGRect paddleRect = CGRectMake((self.view.frame.size.width / 2), 30, 100, 10);
self.paddleViewAI = [[UIView alloc] initWithFrame:paddleRect];
self.paddleViewAI.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.paddleViewAI];
//make AI work
//track location of ball
[self addObserver:self forKeyPath:@"self.ballView.center" options:NSKeyValueObservingOptionNew context:nil];
}
最后我在球移动时响应球位置的变化
-(void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *,id> *)change context:(nullable void *)context {
if ([keyPath isEqualToString:@"self.ballView.center"]) {
if (!CGRectContainsRect(self.view.frame, self.ballView.frame)) {
[self createBall];
[self createCollisions];
}
CGPoint location = self.ballView.center;
//paddle respond to location of ball
if (self.dxAI == 0) {
self.dxAI = location.x - self.paddleViewAI.center.x;
}
//create offsets
CGPoint newLocation = CGPointMake(location.x - self.dxAI, self.paddleViewAI.center.y);
CGRect newRect = CGRectMake(newLocation.x - (self.paddleViewAI.frame.size.width / 2), self.paddleViewAI.frame.origin.y, self.paddleViewAI.frame.size.width, self.paddleViewAI.frame.size.height);
//keep paddle inside view
if (CGRectContainsRect(self.view.frame, newRect)) {
//apply offsets
if (self.attach != nil) {
[self.animator removeBehavior:self.attach];
}
self.attach = [[UIAttachmentBehavior alloc] initWithItem:self.paddleViewAI attachedToAnchor:self.paddleViewAI.center];
// the plan was adjust the frequency to affect speed so AI will only be able to keep up sometimes
self.attach.frequency = 20;
self.attach.damping = 1;
[self.attach setAnchorPoint:newLocation];
[self.animator addBehavior:self.attach];
}
//update animations
[self.animator updateItemUsingCurrentState:self.paddleViewAI];
}
}
我可以在最后一个方法中使用 UISnap、UIAttach 或 UIView animateWithDuration,但正如我提到的 none 非常有效。我对任何建议持开放态度,或者只是让 UIAttach 至少工作会很可爱。
好吧,我从来没有让 UIAttachmentBehavior 工作,但我想出了如何让我的 AI 好但不完美。我最终使用了 UIPushBehavior。如果球在左边,我将 AI 桨推到左边,如果它在右边,则相同。这样我就可以修改桨的推动幅度和阻力来创造我想要的效果。
if (self.pushAI != nil) {
[self.animator removeBehavior:self.pushAI];
}
//keep paddle inside view
if (CGRectContainsRect(self.view.frame, newRect)) {
//apply offsets
self.pushAI = [[UIPushBehavior alloc] initWithItems:@[self.paddleViewAI] mode:UIPushBehaviorModeInstantaneous];
if (self.paddleViewAI.center.x < self.ballView.center.x) {
[self.pushAI setAngle:0 magnitude:self.difficultyMagnitude];
[self.animator addBehavior:self.pushAI];
}
if (self.paddleViewAI.center.x > self.ballView.center.x) {
[self.pushAI setAngle:M_PI magnitude:self.difficultyMagnitude];
[self.animator addBehavior:self.pushAI];
}
}
//update animations
[self.animator updateItemUsingCurrentState:self.paddleViewAI];
所以我正在尝试实现一个基本的 pong AI,它很不错,但可以被打败。我试过 [UIView animateWithDuration...] UISnapBehavior 和 UIAttachmentBehavior。
animateWithDuration 没有正确更新碰撞检测,所以我得到了隐形墙
UISnapBehavior 还是太快了,不响应摩擦或阻力减速
UIAttachmentBehavior 似乎是我最好的选择,因为它适用于我的碰撞行为,如 Snap,但更灵活。我打算调整频率来影响速度。但是,我无法让它工作。欢迎所有建议,这里是代码:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[self startGame];
}
让一切顺利进行的基本方法
- (void)startGame {
[self createBall];
[self createPlayerPaddle];
[self createAIPaddle];
[self createCollisions];
// Remove rotation
self.paddleDynamicProperties = [[UIDynamicItemBehavior alloc] initWithItems:@[self.paddleView, self.paddleViewAI]];
self.paddleDynamicProperties.allowsRotation = NO;
//make heavy
self.paddleDynamicProperties.density = 1000.0f;
//make slow
self.paddleDynamicProperties.friction = 1000;
[self.animator addBehavior:self.paddleDynamicProperties];
}
我们将看看碰撞和 paddleViewAI
- (void)createCollisions {
self.collider = [[UICollisionBehavior alloc] initWithItems:@[self.ballView, self.paddleView, self.paddleViewAI]];
//self.collider.collisionDelegate = self.paddleView;
self.collider.collisionMode = UICollisionBehaviorModeEverything;
[self.collider addBoundaryWithIdentifier:@"left" fromPoint:CGPointMake(0, 0) toPoint:CGPointMake(0, self.view.frame.size.height)];
[self.collider addBoundaryWithIdentifier:@"right" fromPoint:CGPointMake(self.view.frame.size.width, 0) toPoint:CGPointMake(self.view.frame.size.width, self.view.frame.size.height)];
[self.animator addBehavior:self.collider];
}
- (void)createAIPaddle {
CGRect paddleRect = CGRectMake((self.view.frame.size.width / 2), 30, 100, 10);
self.paddleViewAI = [[UIView alloc] initWithFrame:paddleRect];
self.paddleViewAI.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.paddleViewAI];
//make AI work
//track location of ball
[self addObserver:self forKeyPath:@"self.ballView.center" options:NSKeyValueObservingOptionNew context:nil];
}
最后我在球移动时响应球位置的变化
-(void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *,id> *)change context:(nullable void *)context {
if ([keyPath isEqualToString:@"self.ballView.center"]) {
if (!CGRectContainsRect(self.view.frame, self.ballView.frame)) {
[self createBall];
[self createCollisions];
}
CGPoint location = self.ballView.center;
//paddle respond to location of ball
if (self.dxAI == 0) {
self.dxAI = location.x - self.paddleViewAI.center.x;
}
//create offsets
CGPoint newLocation = CGPointMake(location.x - self.dxAI, self.paddleViewAI.center.y);
CGRect newRect = CGRectMake(newLocation.x - (self.paddleViewAI.frame.size.width / 2), self.paddleViewAI.frame.origin.y, self.paddleViewAI.frame.size.width, self.paddleViewAI.frame.size.height);
//keep paddle inside view
if (CGRectContainsRect(self.view.frame, newRect)) {
//apply offsets
if (self.attach != nil) {
[self.animator removeBehavior:self.attach];
}
self.attach = [[UIAttachmentBehavior alloc] initWithItem:self.paddleViewAI attachedToAnchor:self.paddleViewAI.center];
// the plan was adjust the frequency to affect speed so AI will only be able to keep up sometimes
self.attach.frequency = 20;
self.attach.damping = 1;
[self.attach setAnchorPoint:newLocation];
[self.animator addBehavior:self.attach];
}
//update animations
[self.animator updateItemUsingCurrentState:self.paddleViewAI];
}
}
我可以在最后一个方法中使用 UISnap、UIAttach 或 UIView animateWithDuration,但正如我提到的 none 非常有效。我对任何建议持开放态度,或者只是让 UIAttach 至少工作会很可爱。
好吧,我从来没有让 UIAttachmentBehavior 工作,但我想出了如何让我的 AI 好但不完美。我最终使用了 UIPushBehavior。如果球在左边,我将 AI 桨推到左边,如果它在右边,则相同。这样我就可以修改桨的推动幅度和阻力来创造我想要的效果。
if (self.pushAI != nil) {
[self.animator removeBehavior:self.pushAI];
}
//keep paddle inside view
if (CGRectContainsRect(self.view.frame, newRect)) {
//apply offsets
self.pushAI = [[UIPushBehavior alloc] initWithItems:@[self.paddleViewAI] mode:UIPushBehaviorModeInstantaneous];
if (self.paddleViewAI.center.x < self.ballView.center.x) {
[self.pushAI setAngle:0 magnitude:self.difficultyMagnitude];
[self.animator addBehavior:self.pushAI];
}
if (self.paddleViewAI.center.x > self.ballView.center.x) {
[self.pushAI setAngle:M_PI magnitude:self.difficultyMagnitude];
[self.animator addBehavior:self.pushAI];
}
}
//update animations
[self.animator updateItemUsingCurrentState:self.paddleViewAI];