WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

Cocos2d-x の EditBox で複数行表示する

おつかれさまです。エンジニアの藤澤です。

最近 Cocos2d-x の EditBox で比較的長い文章を入力したいケースがあったのですが、標準の EditBox では複数行表示に対応していないようで、文章が見切れてしまったため少々手を加えて対応しました。今回はそのときの内容を書いてみようと思います。

この記事は Cocos2d-x v3.2 をもとに書いていますが、v3.9 からは標準で複数行に対応しているようです(泣)

f:id:wp-fujisawa:20170718173454p:plainf:id:wp-fujisawa:20170718173502p:plain

下準備

標準の EditBox に手を加えるのに抵抗がある場合、自分の project に EditBox をコピーして標準のものと使い分けるのも手だと思います。必要なファイルは以下のとおりです。

  • extensions/GUI/CCEditBox/CCEditBox.h
  • extensions/GUI/CCEditBox/CCEditBox.cpp
  • extensions/GUI/CCEditBox/CCEditBoxImpl.h
  • extensions/GUI/CCEditBox/CCEditBoxImplIOS.h
  • extensions/GUI/CCEditBox/CCEditBoxImplIOS.mm
  • extensions/GUI/CCEditBox/CCEditBoxImplAndroid.h
  • extensions/GUI/CCEditBox/CCEditBoxImplAndroid.mm

コピーしたらまず namespace を独自のものに変更します。おもに NS_CC_EXT_BEGINNS_CC_EXT_END のところを変更するのと、それに伴いコンパイルエラーになるところを直してあげればよいです。

#define getEditBoxImplIOS() ((cocos2d::extension::EditBoxImplIOS*)editBox_)

のところも忘れずに……。

つぎに、CCEditBoxImpl.h の以下の関数を、自前の EditBoxImpl を返すように変更してやります(iOS, Android とも)

extern EditBoxImpl* __createSystemEditBox(EditBox* pEditBox);

最後に、CCEditBoxImplIOS.h の CCCustomUITextField, CCEditBoxImplIOS_objc が二重定義で怒られるのでいったんコメントアウトしてオリジナルの方を見るようにしておきます(あとで変更しますが)

iOS

iOS では内部的に UITextField を使用しているため、 UITextView を使うように変更します。

- @interface CCCustomUITextField : UITextField
+ @interface CustomUITextField : UITextView

これにあわせて UITextFieldDelegateUITextViewDelegate に変更します。

- @interface CCEditBoxImplIOS_objc : NSObject <UITextFieldDelegate>
+ @interface EditBoxImplIOS_objc : NSObject <UITextViewDelegate>

また、 UITextFieldUITextView で互換性のないところも適宜変更します。

        [textField_ setTextColor:[UIColor whiteColor]];
        textField_.font = [UIFont systemFontOfSize:frameRect.size.height*2/3]; //TODO need to delete hard code here.
-         textField_.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        textField_.backgroundColor = [UIColor clearColor];
-         textField_.borderStyle = UITextBorderStyleNone;
        textField_.delegate = self;
        textField_.hidden = true;
        textField_.returnKeyType = UIReturnKeyDefault;
-         [textField_ addTarget:self action:@selector(textChanged) forControlEvents:UIControlEventEditingChanged];
        self.editBox = editBox;
- - (BOOL)textFieldShouldBeginEditing:(UITextField *)sender
+ - (BOOL)textViewShouldBeginEditing:(UITextView *)sender
- - (BOOL)textFieldShouldEndEditing:(UITextField *)sender
+ - (BOOL)textViewShouldEndEditing:(UITextView *)sender
- - (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
- {
-     if (getEditBoxImplIOS()->getMaxLength() < 0)
-     {
-         return YES;
-     }
-     
-     NSUInteger oldLength = [textField.text length];
-     NSUInteger replacementLength = [string length];
-     NSUInteger rangeLength = range.length;
-     
-     NSUInteger newLength = oldLength - rangeLength + replacementLength;
-     
-     return newLength <= getEditBoxImplIOS()->getMaxLength();
- }
+ - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
+ {
+     if ([text isEqualToString:@"\n"])
+     {
+         [textView resignFirstResponder];
+         return NO;
+     }
+     
+     if (getEditBoxImplIOS()->getMaxLength() >= 0)
+     {
+         NSUInteger oldLength = [textView.text length];
+         NSUInteger replacementLength = [text length];
+         NSUInteger rangeLength = range.length;
+         
+         NSUInteger newLength = oldLength - rangeLength + replacementLength;
+         
+         if (newLength > getEditBoxImplIOS()->getMaxLength())
+             return NO;
+     }
+     
+     [self textChanged];
+     
+     return YES;
+ }
void EditBoxImplIOS::setPlaceHolder(const char* pText)
{
-     _systemControl.textField.placeholder = [NSString stringWithUTF8String:pText];
    _labelPlaceHolder->setString(pText);
}

このままだと EditBox の領域外をタップしてもフォーカスが外れないので、platform/ios/CCEAGLView.mm をちょっといじってやる必要があります。

- if([view isKindOfClass:NSClassFromString(@"CCCustomUITextField")])
+ if([view isKindOfClass:NSClassFromString(@"CCCustomUITextField")] || [view isKindOfClass:NSClassFromString(@"CustomUITextField")])

これで入力中のテキストが折り返し表示されるようになりましたが、編集モードを終わるとテキストがセンター寄せ & 見切れてしまいますので、表示を調整します。

    _label = Label::create();
    _label->setAnchorPoint(Vec2(0, 0.5f));
    _label->setColor(Color3B::WHITE);
    _label->setVisible(false);
+    _label->setDimensions(size.width - CC_EDIT_BOX_PADDING * 2, size.height - CC_EDIT_BOX_PADDING * 2);
    _editBox->addChild(_label, kLabelZOrder);
    
    _labelPlaceHolder = Label::create();
    // align the text vertically center
    _labelPlaceHolder->setAnchorPoint(Vec2(0, 0.5f));
    _labelPlaceHolder->setColor(Color3B::GRAY);
+    _labelPlaceHolder->setDimensions(size.width - CC_EDIT_BOX_PADDING * 2, size.height - CC_EDIT_BOX_PADDING * 2);
    _editBox->addChild(_labelPlaceHolder, kLabelZOrder);

これでいい感じに表示されるようになりました。

Android

Android のほうは入力中のテキストは標準で折り返し表示されるので、編集モード以外の表示を調整してやれば OK です。

    _label = Label::create();
    _label->setSystemFontSize(size.height-12);
    // align the text vertically center
    _label->setAnchorPoint(Vec2(0, 0.5f));
    _label->setPosition(Vec2(CC_EDIT_BOX_PADDING, size.height / 2.0f));
    _label->setColor(_colText);
+     _label->setDimensions(size.width - CC_EDIT_BOX_PADDING * 2, size.height - CC_EDIT_BOX_PADDING * 2);
    _editBox->addChild(_label);
    
    _labelPlaceHolder = Label::create();
    _labelPlaceHolder->setSystemFontSize(size.height-12);
    // align the text vertically center
    _labelPlaceHolder->setAnchorPoint(Vec2(0, 0.5f));
    _labelPlaceHolder->setPosition(Vec2(CC_EDIT_BOX_PADDING, size.height / 2.0f));
    _labelPlaceHolder->setVisible(false);
    _labelPlaceHolder->setColor(_colPlaceHolder);
+     _labelPlaceHolder->setDimensions(size.width - CC_EDIT_BOX_PADDING * 2, size.height - CC_EDIT_BOX_PADDING * 2);
    _editBox->addChild(_labelPlaceHolder);

以上です。