サウンドノベル風の文章表示

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Chalaza

サウンドノベル風の文章表示

#1

投稿記事 by Chalaza » 8年前

DXライブラリ公式ページのサンプルプログラムの、『19.マルチタスク風プログラム基本』
http://homepage2.nifty.com/natupaji/DxL ... m.html#N19)を参考にして、
Talk.cppというファイルに同じようなプログラムをアレンジして書いてみたのですが、実行してみると
文字がしっかり1文字づつ表示されているのですが、次の文字が表示されるとすぐ消えてしまいます。
画面にしっかり文章を1文づつ表示させるにはどう直せば良いでしょうか。

Talk.cpp

コード:

 
#include "DxLib.h"

int FrameTime ;	// 前回フレームでの経過時間



// サウンドノベル風文字列描画プログラム用データ群

// 文字のサイズ
#define MOJI_SIZE 24

int DrawPointX , DrawPointY ;	// 文字列描画の位置
int SP , CP ;			// 参照する文字列番号と文字列中の文字ポインタ
int TimeCounter1 ;		// サウンドノベル風文字列描画処理用の時間計測用カウンタ変数
int EndFlag ;			// 終了フラグ
int KeyWaitFlag ;		// ボタン押し待ちフラグ
int OldTime , NowTime ;

char String[][ 256 ] =
{
	" ゲームプログラムとは、いやプログラムとは" ,
	"ある事柄を実現するプログラムの方法を説明されても理解できないことがある。B" ,
	"@ なぜならそのプログラム技法も何かの基本的な技法の組み合わせで出来ているからだ。B",
	"@ これはその他の学問も基本がわからないと応用が利かないということと同じ現象で、",
	"別に特に珍しいことでもない。B" ,
	"C しかしゲームプログラムとなると覚えなくてはならない基礎が沢山あり、" ,
	"さらにある程度クオリティの高いソフトを作ろうとすると色々なプログラム技法を",
	"習得しなくてはならない。B" ,
	"@ しかもある程度レベルが高くなると自分で技法を編み出すか、技術レベルの高い",
	"プログラマーに聞くなどするしか方法がなく大変厄介である。B"
	"というかそのせいでゲームプログラムの敷居は高くなっているといえる。BE"
} ;

void StringShred( void ) ;	// サウンドノベル風文字列描画プログラムの処理関数
void Kaigyou( void ) ;		// サウンドノベル風文字列描画プログラムで使用する改行関数

void Talk_Initialize(){

	// 描画位置の初期位置セット
	DrawPointX = 0 ;
	DrawPointY = 0 ;

	// 参照文字位置をセット
	SP = 0 ;	// 1行目の
	CP = 0 ;	// 0文字

	// フォントのサイズセット
	SetFontSize( MOJI_SIZE ) ;

	// 終了フラグを倒す
	EndFlag = 0 ;

	// 現在時間を初期化
	NowTime = GetNowCount() ;


}

void Talk_Draw(){

	// サウンドノベル風文字列描画処理を行う(画面右側のみ描画出きるようにする)
	StringShred() ;

	// 時間待ち
	WaitTimer( 17 ) ;

	// 今フレームでの経過時間を計測
	OldTime = NowTime ;
	NowTime = GetNowCount() ;
	FrameTime = NowTime - OldTime ;
}

// サウンドノベル風文字列描画プログラムの処理関数
void StringShred( void )
{
	char OneMojiBuf[ 3 ] ;	// 1文字分一時記憶配列

	// 時間計測用変数の値を1減らす
	TimeCounter1 -- ;

	// 時間計測用変数が0以上か、終了フラグが1だった場合は処理をしない
	if( TimeCounter1 < 0 && EndFlag == 0 )
	{
		char  Moji ;

		// ボタン押し待ちフラグがたっていた場合はボタンが押されるまでここで終了
		if( KeyWaitFlag == 1 )
		{
			if( CheckHitKeyAll() != 0 ) 
			{
				// ボタンが押されていたら解除
				KeyWaitFlag = 0 ;
			}
		}
		else
		{
			// 文字の描画
			Moji = String[ SP ][ CP ] ;
			switch( Moji )
			{
			case '@' :	// 改行文字

				// 改行処理および参照文字位置を一つ進める
				Kaigyou() ;
				CP ++ ;

				break ;

			case 'B' :	// ボタン押し待ち文字

				// ボタン押し待ちフラグをたてる
				KeyWaitFlag = 1 ;
				CP ++ ;

				break ;

			case 'E' :	// 終了文字

				// 終了フラグを立てるおよび参照文字位置を一つ進める
				EndFlag = 1 ;
				CP ++ ;

				break ;

			case 'C' :	// クリア文字

				// 画面を初期化して描画文字位置を初期位置に戻すおよび参照文字位置を一つ進める
				DrawBox( 0 , 0 , 512 , 384 , 0 , TRUE ) ;
				DrawPointY = 0 ;
				DrawPointX = 0 ;
				CP ++ ;

				break ;

			default :	// その他の文字

				// 1文字分抜き出す
				OneMojiBuf[ 0 ] = String[ SP ][ CP ] ;
				OneMojiBuf[ 1 ] = String[ SP ][ CP + 1 ] ;
				OneMojiBuf[ 2 ] = '\0' ;

				// 1文字描画
				DrawString( 0 + DrawPointX * MOJI_SIZE , DrawPointY * MOJI_SIZE ,
					OneMojiBuf , GetColor( 255 , 255 , 255 ) ) ;

				// 参照文字位置を2バイト勧める
				CP += 2 ;

				// カーソルを一文字文進める
				DrawPointX ++ ;

				// 画面からはみ出たら改行する
				if( DrawPointX * MOJI_SIZE + MOJI_SIZE > 512 ) Kaigyou() ;

				break ;
			}

			// 参照文字列の終端まで行っていたら参照文字列を進める
			if( String[ SP ][ CP ] == '\0' )
			{
				SP ++ ;
				CP = 0 ;
			}
		}

		// 時間計測用変数の値をセットする
		TimeCounter1 = 2 ;
	}
}

// 改行関数
void Kaigyou( void )
{
	int TempGraph ;

	// 描画行位置を一つ下げる
	DrawPointY ++ ;

	// 描画列を最初に戻す
	DrawPointX = 0 ;

	// もし画面からはみ出るなら画面をスクロールさせる
	if( DrawPointY * MOJI_SIZE + MOJI_SIZE > 384 )
	{
		// テンポラリグラフィックの作成
		TempGraph = MakeGraph( 0 , 384 ) ;

		// 画面の内容を丸々コピーする
		GetDrawScreenGraph( 0 , 0 , 512 , 384 , TempGraph ) ;

		// 一行分上に貼り付ける
		DrawGraph( 0 , -MOJI_SIZE ,TempGraph , FALSE ) ;
		
		// 一番下の行の部分を黒で埋める
		DrawBox( 0 , 384 - MOJI_SIZE , 512 , 384 , 0 , TRUE ) ;

		// 描画行位置を一つあげる
		DrawPointY -- ;

		// グラフィックを削除する
		DeleteGraph( TempGraph ) ;
	}
}

Talk.h

コード:

 
#ifndef DEF_TALK_H //二重include防止

#define DEF_TALK_H

void StringShred( void ) ;	// サウンドノベル風文字列描画プログラムの処理関数

void Kaigyou( void ) ;		// サウンドノベル風文字列描画プログラムで使用する改行関数

void Talk_Initialize();		//初期化

void Talk_Draw();	//文章表示関数

#endif 
main.cpp

コード:

 
#include "DxLib.h"
#include "FPlayer.h"
#include "Keyboard.h"
#include "FMap.h"
#include "Talk.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	SetMainWindowText("RPG");
	ChangeWindowMode(TRUE);
	SetGraphMode(512,384,32);
	if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化


	//ジョイパ使用
	int GetJoypadInputState( int InputType );

	InputCount_t InputCount;
	FPlayer_t FPlayer;
	ObjectData_t ObjectData;

	FPlayer_Initialize( &FPlayer );	//Fプレイヤー初期化
	FMap_Initialize( &ObjectData , &FPlayer );
	Talk_Initialize();	//文章表示の初期化

	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

		Keyboard_Update();	//キーボード使用
		Input_Count(&InputCount);//入力時間をカウントに

		FPlayer_Calc( &FPlayer, &InputCount);	//Fプレイヤー計算
		FMAP_Object( FPlayer , ObjectData);		//オブジェクト位置計算
		FMap_Scroll( &FPlayer );	//Fマップスクロール

		FMap_Draw(FPlayer);		//Fマップ描画

		Talk_Draw();	//文章を表示する

	}

	FPlayer_Finalize( FPlayer );  // 終了処理

        DxLib_End();
        return 0;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#2

投稿記事 by softya(ソフト屋) » 8年前

ClearDrawScreen()で消されるんでしょうね。

こちらのが参考になると思います。
「ゲーム作りで学ぶ! 実践的C言語プログラミング - karetta.jp」
http://karetta.jp/book-cover/game-programming
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Chalaza

Re: サウンドノベル風の文章表示

#3

投稿記事 by Chalaza » 8年前

早速紹介してもらった「ゲーム作りで学ぶ! 実践的C言語プログラミング - karetta.jp」の
サウンドノベル風メッセージプログラムを参考にTalk.cppを作りなおしました。
#include <string.h>を入れから、『strncpyを使うのは危険なのでstrncpy_sを使え』と警告が出たので
strncpy_sに変更したところまでは良いのですが、エラーはでなかったのですが文字をキー入力でセットしても表示されません。
キー入力で表示する文字を変えるのはMessageという関数なのですが、このTalk.cppのどこが原因でしょうか?

コード:

#include "DxLib.h"
#include <string.h>

int isJapaneseCharacter(unsigned char code);
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine);
void drawMessage();
void initGame();

//メッセージのフォントの大きさ
#define MESSAGE_FONT_SIZE 20
//仮想バッファの最大文字数
#define MESSAGE_MAX_LENGTH 30
//仮想バッファの最大行数
#define MESSAGE_MAX_LINE 5
//メッセージボックスのX座標
#define MESSAGE_BOX_X_POS 40
//メッセージボックスのY座標
#define MESSAGE_BOX_Y_POS 320
//メッセージボックスの画像ファイル
#define MESSAGE_BOX_GRAPHIC_FILENAME "./Pic/boxd3.jpg"

//表示したいメッセージ
char g_message[MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE];

//画面にメッセージを表示する際にしようする仮想テキストバッファ
char g_messageBuffer[MESSAGE_MAX_LINE][MESSAGE_MAX_LENGTH];

//メッセージボックス関係

//現在何文字目までを表示しているか
static int g_currentCursor = 0;
//何行目の文字を表示しているか
static int g_currentLineCursor = 0;
//白
static int g_whiteColor;
//黒
static int g_blackColor;
//メッセージボックスの画像
static int g_messageBoxGraphicHandle;


//code が日本語であるか判定する
//戻り値 1:日本語 0:日本語ではない
int isJapaneseCharacter(unsigned char code)
{
        if( (code >= 0x81 && code <= 0x9F) || 
                (code >= 0xE0 && code <= 0xFC) ) {
                        return 1;
        }
        return 0;
}


//messageで指定した文章を start の位置から len 文字分表示する
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine)
{
        int i;
        //文字数
        int maxLen = strlen( message );
        
        //startの位置を変更する
        //startの位置までに日本語がでてきていたら,1を足していく
        for( i = 0; i < start && message[i] != '\0'; ) {
                if( isJapaneseCharacter( message[i] ) ) {
                        //日本語の場合,2バイト分すすめる
                        i += 2;
                        //startに1バイト分足す
                        start++;
                }else {
                        //半角文字の場合,1バイト分進める
                        i++;
                }
        }

        //startの位置が表示したい最大文字数より大きい場合
        if( start >= maxLen ) {
                return;
        }

        //指定した位置からlen文字分表示する
        for( i = 0; i < len && message[ start + i ] != '\0'; ) {
                if( isJapaneseCharacter( message[ start + i ] ) ) {
                        //日本語の場合,2文字分bufferにセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        g_messageBuffer[ bufferLine ][ i + 1 ] = message[ start + i + 1 ];
                        //lenは日本語なので,1バイト分追加する
                        len++;
                        //2バイト分進める
                        i += 2;
                }else {
                        //半角文字1文字をセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        //1バイト分進める
                        i++;
                }
        }
        g_messageBuffer[ bufferLine ][i] = '\0';

        //メッセージ描画
        DrawString(posX, posY, g_messageBuffer[ bufferLine ], color );
}

//メッセージ描画
void Talk_Draw()
{
        int i;

        //文字が1文字もセットされていなかったらメッセージボックスを表示しない
        if( strnlen(g_message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE ) <= 0 ) {
                return;
        }

        //メッセージボックス描画
        DrawGraph( MESSAGE_BOX_X_POS, MESSAGE_BOX_Y_POS, g_messageBoxGraphicHandle, FALSE );
        
        if( g_message[g_currentCursor] != '\0' ) {
                g_currentCursor++;
        }

        //MESSAGE_MAX_LENGTH まで文字を描画したら段落を切り替える
        if( g_currentCursor % MESSAGE_MAX_LENGTH == 0 ) {
                if( g_message[g_currentCursor] != '\0' ) {
                        g_currentLineCursor++;
                }
        }

        //メッセージ描画部分
        for( i = 0; i < MESSAGE_MAX_LINE; i++ ) {
                if( i == g_currentLineCursor ) {
                        //メッセージ風に表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                g_currentCursor - MESSAGE_MAX_LENGTH * i,
                                MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_blackColor, i );
                        break;
                }else {
                        //メッセージをそのまま表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                MESSAGE_MAX_LENGTH, MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_blackColor, i );
                }
        }
}

//初期化処理
void Talk_Initialize()
{
        //白
        g_whiteColor = GetColor(255,255,255);
        //黒
        g_blackColor = GetColor(0, 0, 0);
        //メッセージボックス
        g_messageBoxGraphicHandle = LoadGraph( MESSAGE_BOX_GRAPHIC_FILENAME );

}

//描画したいメッセージをセット
void setMessage(const char* message)
{
        //カーソルを初期化
        g_currentCursor = 0;
        g_currentLineCursor = 0;

        //メッセージをコピー
        strncpy_s( g_message, message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE);
}

void Message(){
	//F1 F2を押すとメッセージをセットする
	//F3を押すと空の文字列をセットする

	if( CheckHitKey( KEY_INPUT_F1 ) ) {
		setMessage("はろーわーるど");
	}else if( CheckHitKey( KEY_INPUT_F2 ) ) {
		setMessage("HELLO WORLD!あいうえおかきくけこさしすせそたちつてとな" 
			"にぬねのはひふへほまみむめもやゆよらりるれろわをん");
	}else if( CheckHitKey( KEY_INPUT_F3 ) ) {
		setMessage("");
	}


}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#4

投稿記事 by softya(ソフト屋) » 8年前

そのまま動かせるものではないみたいなので、こちらで確認が難しいです。
デバッガ等でまず自分で追いかけてみてください。
「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&b=982&c=2

どうしても分からなければ、ソースコード全部をzipで何処かにアップロードしてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Chalaza

Re: サウンドノベル風の文章表示

#5

投稿記事 by Chalaza » 8年前

表示開始するする文字の位置がずれてて見えなかっただけでした。申し訳ありません。
なんとか文字を表示することができました。ありがとうございます。

このままでも良いのですが、文章の内容から判断して(記号で判断する等)改行をするようにしてみたいのですが、
たとえば、文章(message)の中に'@'があったら改行するようにするには、
if( message != '@')で改行。みたいにすれば良いと思うのですが、改行させる処理を書く位置がよくわからなく、
画面が止まったりしてしまいます。
@で改行させるように組み込むにはどこに書けばよいでしょうか?

このままでは画面が止まります…

コード:

#include "DxLib.h"
#include <string.h>

int isJapaneseCharacter(unsigned char code);
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine);
void drawMessage();
void initGame();

//メッセージのフォントの大きさ
#define MESSAGE_FONT_SIZE 20
//仮想バッファの最大文字数
#define MESSAGE_MAX_LENGTH 30
//仮想バッファの最大行数
#define MESSAGE_MAX_LINE 5
//メッセージボックスのX座標
#define MESSAGE_BOX_X_POS 96
//メッセージボックスのY座標
#define MESSAGE_BOX_Y_POS 288
//メッセージボックスの画像ファイル
#define MESSAGE_BOX_GRAPHIC_FILENAME "./Pic/boxd3.jpg"

//表示したいメッセージ
char g_message[MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE];

//画面にメッセージを表示する際にしようする仮想テキストバッファ
char g_messageBuffer[MESSAGE_MAX_LINE][MESSAGE_MAX_LENGTH];

//メッセージボックス関係

//現在何文字目までを表示しているか
static int g_currentCursor = 0;
//何行目の文字を表示しているか
static int g_currentLineCursor = 0;
//白
static int g_whiteColor;
//黒
static int g_blackColor;
//メッセージボックスの画像
static int g_messageBoxGraphicHandle;
static int Moji;

//code が日本語であるか判定する
//戻り値 1:日本語 0:日本語ではない
int isJapaneseCharacter(unsigned char code)
{
        if( (code >= 0x81 && code <= 0x9F) || 
                (code >= 0xE0 && code <= 0xFC) ) {
                        return 1;
        }
        return 0;
}


//messageで指定した文章を start の位置から len 文字分表示する
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine)
{

        int i;
        //文字数
        int maxLen = strlen( message );
        


        //startの位置を変更する
        //startの位置までに日本語がでてきていたら,1を足していく
        for( i = 0; i < start && message[i] != '\0'; ) {
                if( isJapaneseCharacter( message[i] ) ) {
                        //日本語の場合,2バイト分すすめる
                        i += 2;
                        //startに1バイト分足す
                        start++;
                }else if(message[i] != '@'){//文章に@があったら改行
						g_currentLineCursor++;
				}else {
                        //半角文字の場合,1バイト分進める
                        i++;
                }
        }

		//MESSAGE_MAX_LENGTH まで文字を描画したら段落を切り替える
        if( g_currentCursor % MESSAGE_MAX_LENGTH == 0 ) {
                if( g_message[g_currentCursor] != '\0' ) {
                        g_currentLineCursor++;
                }
        }

        //startの位置が表示したい最大文字数より大きい場合
        if( start >= maxLen ) {
                return;
        }

        //指定した位置からlen文字分表示する
        for( i = 0; i < len && message[ start + i ] != '\0'; ) {
                if( isJapaneseCharacter( message[ start + i ] ) ) {
                        //日本語の場合,2文字分bufferにセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        g_messageBuffer[ bufferLine ][ i + 1 ] = message[ start + i + 1 ];
                        //lenは日本語なので,1バイト分追加する
                        len++;
                        //2バイト分進める
                        i += 2;
                }else {
                        //半角文字1文字をセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        //1バイト分進める
                        i++;
                }
        }
        g_messageBuffer[ bufferLine ][i] = '\0';


        //メッセージ描画
        DrawString(posX, posY, g_messageBuffer[ bufferLine ], color );
}

//メッセージ描画
void Talk_Draw()
{
        int i;

        //文字が1文字もセットされていなかったらメッセージボックスを表示しない
        if( strnlen(g_message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE ) <= 0 ) {
                return;
        }

        //メッセージボックス描画
        DrawGraph( MESSAGE_BOX_X_POS, MESSAGE_BOX_Y_POS, g_messageBoxGraphicHandle, FALSE );
        
        if( g_message[g_currentCursor] != '\0' ) {
                g_currentCursor++;
        }


        //メッセージ描画部分
        for( i = 0; i < MESSAGE_MAX_LINE; i++ ) {
                if( i == g_currentLineCursor ) {
                        //メッセージ風に表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                g_currentCursor - MESSAGE_MAX_LENGTH * i,
                                MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_whiteColor, i );
                        break;
                }else {
                        //メッセージをそのまま表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                MESSAGE_MAX_LENGTH, MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_whiteColor, i );
                }
        }
}

//初期化処理
void Talk_Initialize()
{
        //白
        g_whiteColor = GetColor(255,255,255);
        //黒
        g_blackColor = GetColor(0, 0, 0);
        //メッセージボックス
        g_messageBoxGraphicHandle = LoadGraph( MESSAGE_BOX_GRAPHIC_FILENAME );

}

//描画したいメッセージをセット
void setMessage(const char* message)
{
        //カーソルを初期化
        g_currentCursor = 0;
        g_currentLineCursor = 0;

        //メッセージをコピー
        strncpy_s( g_message, message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE);
}

void Message(){
	//F1 F2を押すとメッセージをセットする
	//F3を押すと空の文字列をセットする

	if( CheckHitKey( KEY_INPUT_F1 ) ) {
		setMessage("あああああ@"
			"いいいいいい@"
			"うううううううう");
	}else if( CheckHitKey( KEY_INPUT_F2 ) ) {
		setMessage("HELLO WORLD!あいうえおかきくけこさしすせそたちつてとな" 
			"にぬねのはひふへほまみむめもやゆよらりるれろわをん");
	}else if( CheckHitKey( KEY_INPUT_F3 ) ) {
		setMessage("");
	}


}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#6

投稿記事 by softya(ソフト屋) » 8年前

@でg_currentLineCursor++;すれば良いはずです。
ただし@マークを表紙しない方法は頭を捻ってみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Chalaza

Re: サウンドノベル風の文章表示

#7

投稿記事 by Chalaza » 8年前

少し直してみました。

コード:

		//MESSAGE_MAX_LENGTH まで文字を描画したら段落を切り替える
        if( g_currentCursor % MESSAGE_MAX_LENGTH == 0 ) {
                if( g_message[g_currentCursor] != '\0' ) {
                        g_currentLineCursor++;
                }
        }
		if( g_message[g_currentCursor] == '@' ) {	//@マークで改行する
                        g_currentLineCursor++;
		}

この2つがあれば、最大文字数でも@でも改行できると思うんですが、画面は止まらなくなったものの、
なぜか@マークが表示された所から文字を表示するスピードが速くなっただけで改行はしてくれませんでした。
でも@マークは認識してくれているようなのですが…
処理を書く位置とかの問題でしょうか?

コード:

#include "DxLib.h"
#include <string.h>

int isJapaneseCharacter(unsigned char code);
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine);
void drawMessage();
void initGame();

int TalkFrame;	//メッセージ枠の画像
int FontHandle;	//フォントハンドル

//メッセージのフォントの大きさ
#define MESSAGE_FONT_SIZE 20
//仮想バッファの最大文字数
#define MESSAGE_MAX_LENGTH 30
//仮想バッファの最大行数
#define MESSAGE_MAX_LINE 5
//メッセージボックスのX座標
#define MESSAGE_BOX_X_POS 112
//メッセージボックスのY座標
#define MESSAGE_BOX_Y_POS 284

//表示したいメッセージ
char g_message[MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE];

//画面にメッセージを表示する際にしようする仮想テキストバッファ
char g_messageBuffer[MESSAGE_MAX_LINE][MESSAGE_MAX_LENGTH];

//メッセージボックス関係

//現在何文字目までを表示しているか
static int g_currentCursor = 0;
//何行目の文字を表示しているか
static int g_currentLineCursor = 0;
//白
static int g_whiteColor;
//黒
static int g_blackColor;
//メッセージボックスの画像
static int g_messageBoxGraphicHandle;
static int Moji;

//code が日本語であるか判定する
//戻り値 1:日本語 0:日本語ではない
int isJapaneseCharacter(unsigned char code)
{
        if( (code >= 0x81 && code <= 0x9F) || 
                (code >= 0xE0 && code <= 0xFC) ) {
                        return 1;
        }
        return 0;
}


//messageで指定した文章を start の位置から len 文字分表示する
void writeSubstring(char* message, int start, int len,
                 int posX, int posY, int color, int bufferLine)
{

        int i;
        //文字数
        int maxLen = strlen( message );
        


        //startの位置を変更する
        //startの位置までに日本語がでてきていたら,1を足していく
        for( i = 0; i < start && message[i] != '\0'; ) {
                if( isJapaneseCharacter( message[i] ) ) {
                        //日本語の場合,2バイト分すすめる
                        i += 2;
                        //startに1バイト分足す
                        start++;
                }else {
                        //半角文字の場合,1バイト分進める
                        i++;
                }
        }

		//MESSAGE_MAX_LENGTH まで文字を描画したら段落を切り替える
        if( g_currentCursor % MESSAGE_MAX_LENGTH == 0 ) {
                if( g_message[g_currentCursor] != '\0' ) {
                        g_currentLineCursor++;
                }
        }
		if( g_message[g_currentCursor] == '@' ) {	//@マークで改行する
                        g_currentLineCursor++;
		}

        //startの位置が表示したい最大文字数より大きい場合
        if( start >= maxLen ) {
                return;
        }

        //指定した位置からlen文字分表示する
        for( i = 0; i < len && message[ start + i ] != '\0'; ) {
                if( isJapaneseCharacter( message[ start + i ] ) ) {
                        //日本語の場合,2文字分bufferにセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        g_messageBuffer[ bufferLine ][ i + 1 ] = message[ start + i + 1 ];
                        //lenは日本語なので,1バイト分追加する
                        len++;
                        //2バイト分進める
                        i += 2;
				}else {
                        //半角文字1文字をセット
                        g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];
                        //1バイト分進める
                        i++;
                }
        }
        g_messageBuffer[ bufferLine ][i] = '\0';


        //メッセージ描画
		DrawStringToHandle( posX , posY , g_messageBuffer[ bufferLine ] , GetColor(255,255,255), FontHandle ) ;
}

//メッセージ描画
void Talk_Draw()
{
        int i;

        //文字が1文字もセットされていなかったらメッセージボックスを表示しない
        if( strnlen(g_message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE ) <= 0 ) {
                return;
        }

        //メッセージボックス描画
        DrawGraph( 0, 288, g_messageBoxGraphicHandle, TRUE );
        
        if( g_message[g_currentCursor] != '\0' ) {
                g_currentCursor++;
        }


        //メッセージ描画部分
        for( i = 0; i < MESSAGE_MAX_LINE; i++ ) {
                if( i == g_currentLineCursor ) {
                        //メッセージ風に表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                g_currentCursor - MESSAGE_MAX_LENGTH * i,
                                MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_whiteColor, i );
                        break;
                }else {
                        //メッセージをそのまま表示
                        writeSubstring( g_message, i * MESSAGE_MAX_LENGTH ,
                                MESSAGE_MAX_LENGTH, MESSAGE_BOX_X_POS + 15,
                                MESSAGE_BOX_Y_POS + MESSAGE_FONT_SIZE * i + 15,
                                g_whiteColor, i );
                }
        }
}

//初期化処理
void Talk_Initialize()
{
        //白
        g_whiteColor = GetColor(255,255,255);
        //黒
        g_blackColor = GetColor(0, 0, 0);
        //メッセージボックス
        g_messageBoxGraphicHandle = LoadGraph( "Image/Frame/TalkFrame.png" );
		FontHandle = CreateFontToHandle("小塚ゴシック Pro L" , 20 , 2 , DX_FONTTYPE_ANTIALIASING);

}

//描画したいメッセージをセット
void setMessage(const char* message)
{
        //カーソルを初期化
        g_currentCursor = 0;
        g_currentLineCursor = 0;

        //メッセージをコピー
        strncpy_s( g_message, message, MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE);
}

void Message(){
	//F1 F2を押すとメッセージをセットする
	//F3を押すと空の文字列をセットする

	if( CheckHitKey( KEY_INPUT_F1 ) ) {
		setMessage("あああああ@"
			"いいいいいい@"
			"てすとてすとてすとてすとてすとてすとてすとてすとてすとてすと");
	}else if( CheckHitKey( KEY_INPUT_F2 ) ) {
		setMessage("HELLO WORLD!あいうえおかきくけこさしすせそたちつてとな" 
			"にぬねのはひふへほまみむめもやゆよらりるれろわをん");
	}else if( CheckHitKey( KEY_INPUT_F3 ) ) {
		setMessage("");
	}


}


Chalaza

Re: サウンドノベル風の文章表示

#8

投稿記事 by Chalaza » 8年前

すみません。文字を表示するスピードが速くなったのは@マークのせいではなかったっぽいです。
2行目位から早くなってしまうようでした。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#9

投稿記事 by softya(ソフト屋) » 8年前

動く全体をいただけると幸いです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#10

投稿記事 by ISLe » 8年前

Talk_Draw関数の描画ループ部分を見ると、このプログラムは、文字列の途中で改行することを想定していないですね。
ここから改造するとなるとかなりめんどうですよ。

あとテキストをびっちり詰め込むと最後に配列の範囲外をアクセスしてしまいます。

Chalaza

Re: サウンドノベル風の文章表示

#11

投稿記事 by Chalaza » 8年前

どっとうpろだというアップローダにアップロードしてみました。
zipの中にDXLibも入っているので解凍など時間がかかると思います。
http://www.dotup.org/uploda/www.dotup.o ... 8.zip.html でSRPGというファイルです。

ここから改行させるよりは、違う方法で書きなおした方が良いのでしょうか…
とりあえず参考になりそうな改行の書き方を探してみます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#12

投稿記事 by softya(ソフト屋) » 8年前

参考となるサンプルは、そのまま書き写すのではなくて自分のものにしないと意味がありませんよ。
自由に分解&再構築が出来るぐらい理解しないとサンプルを書き写したに過ぎないことになります。

それと[SRPG.sdf]は含む必要がないので今後zipにアップする場合は削ってもらったほうが良いと思います。

あと私のRPG講座って紹介済みでしたっけ?
「マイ 日記 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&sd=a&c=2
こちらもメッセージ/メニューのシステムがあるので参考にしてください。

[補足]何とか成るか調べ中。
たしかに「ゲーム作りで学ぶ! 実践的C言語プログラミング」のやつは基本的に1つの文字列は途中で改行を考えていないタイプですねぇ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#13

投稿記事 by softya(ソフト屋) » 8年前

動きをデバッガで見てみました。g_currentCursorで何とか成ると思ったのは、こちらの勘違いでした申し訳ないです。
ISLeさんが書いてますが少なくともTalk_Draw()やwriteSubstring()は組み直さないと無理ですね。
いい勉強にはなると思うので、挑戦されてみてはどうでしょうか?

動作は理解されていますか?
理解さえ出来れば組み替え出来ますので、まず理解に挑戦して分からないことは聞いてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Chalaza

Re: サウンドノベル風の文章表示

#14

投稿記事 by Chalaza » 8年前

組み直しを考えてみて、まず動作を最後まで理解しようと思ったのですが、コメントのおかげで処理の内容は理解できるのですが、
処理の式などで検索してみても分からなかったところがについて聞かせていただきます。

まず、バッファに文字を入れてからバッファの内容を表示しているようですが、
char g_message[MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE]; は、1次元配列で、
char g_messageBuffer[MESSAGE_MAX_LINE][MESSAGE_MAX_LENGTH]; は2次元配列なのに対し、
g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];のように2次元に1次元を代入できているのは何故でしょうか?
また、どういう処理がおこっているのでしょうか?
表示するときは1次元になっているのもよくわかりません…。

それと、この文字を表示するプログラムは、改行するときには、
g_currentLineCursor++;を使って段落を1段下げる処理をvoid Talk_Draw()の中の、メッセージ描画をするwriteSubstringよりも
前に書かないとうまく機能しないことに気付いたのですが、これってg_currentLineCursor++;で改行しないで
writeSubstringの処理の中でバッファを改行した方が良いということでしょうか?

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#15

投稿記事 by ISLe » 8年前

karetta.jpを参考にしたほうは、一気に表示する行もwriteSubstring関数でひと文字ずつ切り出す処理を行なっています。
これはたいへん無駄なので、表示用のテキストバッファに追記する処理と、オリジナルテキスト(スクリプト)からひと文字ずつ切り出す処理を分けてください。

描画処理は、(装飾など無ければ)表示用テキストバッファの中身を単純に表示するだけです。

文字列以外に記憶する必要のある情報は、スクリプトのどこから切り出すか、表示用テキストバッファのどこに追記するか、だけです。

表示用テキストバッファを表示サイズよりも多く行を確保すればそのままいわゆるバックログも実装できます。

必要な部品はいままでのコードにすべてそろっています。
はっきり言って、いままで提示されたコードの半分くらいですっきり書けますので頑張ってください。

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#16

投稿記事 by ISLe » 8年前

Chalaza さんが書きました:char g_message[MESSAGE_MAX_LENGTH * MESSAGE_MAX_LINE]; は、1次元配列で、
char g_messageBuffer[MESSAGE_MAX_LINE][MESSAGE_MAX_LENGTH]; は2次元配列なのに対し、
g_messageBuffer[ bufferLine ][ i ] = message[ start + i ];のように2次元に1次元を代入できているのは何故でしょうか?
g_messageBufferはchar[][]。
g_messageBuffer[ bufferLine ]はchar[]。
g_messageBuffer[ bufferLine ][ i ]はchar。
messageはchar[]。
message[ start + i ]はchar。
char同士なので代入できます。
Chalaza さんが書きました:g_currentLineCursor++;を使って段落を1段下げる処理をvoid Talk_Draw()の中の、メッセージ描画をするwriteSubstringよりも
前に書かないとうまく機能しないことに気付いたのですが、これってg_currentLineCursor++;で改行しないで
writeSubstringの処理の中でバッファを改行した方が良いということでしょうか?
途中で改行することを想定していないからです。
このプログラムは最新行より前の行は横幅いっぱい埋まっていないと破綻します。

Chalaza

Re: サウンドノベル風の文章表示

#17

投稿記事 by Chalaza » 8年前

テキストから1文字ずつ切り出して入れる用の仮想バッファと、表示する用のバッファに分けるということでしょうか?

まずテキストから1文字ずつ仮想バッファに入れていき、それを表示用バッファに入れ直して、表示用バッファの内容を画面に表示していく。
という解釈で合ってますか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#18

投稿記事 by softya(ソフト屋) » 8年前

基本的な考え方は画面に表示されうる文字数が収まる表示用バッファを用意して、文字を特定フレームごとに一文字づつコピーしていきます。
改行があれば行を変更して横がいっぱいでも改行します。できれば、ここで禁則処理も考慮できればなお良いです。
表示処理は、表示用のバッファを表示します。

やるべき事は基本的にはこれだけです。
元の文字列→仮想バッファ→表示バッファ→表示
はややこしいので、
元の文字列→表示バッファ→表示
になるように考えてみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#19

投稿記事 by ISLe » 8年前

簡単なサンプルプログラムを作ってみました。

コード:

#include "DxLib.h"
#include <string.h>

#define FONT_SIZE 24
#define LINES     5
#define COLUMNS   20

// メッセージ表示用テキストのバッファ
char MessageLog[LINES][COLUMNS+1];
// 書き込み対象の行インデックス
int messagelog_line_index;

// 加工前テキストの加工位置を指すポインタ
char *pMessage;

bool isLeadShiftJIS(unsigned char c)
{
	return (c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFC);
}

void setMessage(char *pMsg)
{
	// ログをクリア
	for (int i=0; i<LINES; i++) {
		MessageLog[i][0] = '\0';
	}
	// 先頭行から書き込む
	messagelog_line_index = 0;
	// 加工前テキストのポインタ記憶
	pMessage = pMsg;
}

void Log_LineBreak(void)
{
	messagelog_line_index ++;
	if (messagelog_line_index >= LINES) {
		messagelog_line_index = 0;
	}
	MessageLog[messagelog_line_index][0] = '\0';
}

void Log_Add(char *str) // ※手抜き
{
	int len = strlen(MessageLog[messagelog_line_index]) + strlen(str);
	if (len > COLUMNS) {
		Log_LineBreak();
	}
	strcat(MessageLog[messagelog_line_index], str);
}

void Talk_Proc(void)
{
	if (pMessage == NULL) return;

	char str[3];
	while (str[0] = *pMessage)
	{
		pMessage++;

		if (str[0] == '@') {
			// 改行
			Log_LineBreak();
			continue; // 継続
		}

		if (isLeadShiftJIS(str[0])) {
			// シフトJISの1バイト目のとき
			str[1] = *(pMessage++);
			str[2] = '\0';
		}
		else {
			str[1] = '\0';
		}
		Log_Add(str);
		break;
	}
}

void Talk_Draw(void)
{
	for (int i=0; i<LINES; i++) {
		DrawString(0, FONT_SIZE * i, MessageLog[i], GetColor(255, 255, 255));
	}
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	ChangeWindowMode(TRUE);
	if (DxLib_Init() != 0) return 0;
	SetDrawScreen(DX_SCREEN_BACK);
	SetFontSize(FONT_SIZE);
	while (ProcessMessage() == 0 && ClearDrawScreen() == 0)
	{
		if (CheckHitKey(KEY_INPUT_F1)) {
			setMessage(
				"あああああ@"
				"いいいいいい@"
				"うううううううう"
			);
		} else if (CheckHitKey(KEY_INPUT_F2)) {
			setMessage(
				"HELLO WORLD!あいうえおかきくけこさしすせそたちつてとな"
				"にぬねのはひふへほまみむめもやゆよらりるれろわをん"
			);
		} else if (CheckHitKey(KEY_INPUT_F3)) {
			setMessage("");
		}
		Talk_Proc();
		Talk_Draw();
		ScreenFlip();
	}
	DxLib_End();
	return 0;
}

Chalaza

Re: サウンドノベル風の文章表示

#20

投稿記事 by Chalaza » 8年前

ISleさんのサンプルを参考に、少し改造(?)をして、@で改行の他に
Bでボタン入力待ち、Cで画面クリア、Eで終わり(表示を消す)を実装してみました。
ついでに入力待ちの間は右下で▼が点滅します。
すべてうまく動いてくれたのですが、半角文字のログでの進み具合とは別に、@やBやCのコマンドを
空白を開けずに連続で書くと画面が止まってしまいます。
ゲームの内容的には全く問題ないのですが、制作中にミスをするとバグになりやすいなと思ったので直してみようと思ったのですが、
まず、なぜ@やBのコマンドを続けて書くと止まってしまうのでしょうか?

Chalaza

Re: サウンドノベル風の文章表示

#21

投稿記事 by Chalaza » 8年前

コードはこれです。

コード:

#include "DxLib.h"
#include <string.h>
#include "Keyboard.h"
 
#define FONT_SIZE 20
#define LINES     4
#define COLUMNS   46
 
// メッセージ表示用テキストのバッファ
char MessageLog[LINES][COLUMNS+1];
// 書き込み対象の行インデックス
int messagelog_line_index;
 
// 加工前テキストの加工位置を指すポインタ
char *pMessage;

int MessageBoxImg;	//メッセージボックス画像
int Made1;			//顔グラ
int FontHandle;		//フォントハンドル
int SankakuFont;	//▼のフォントハンドル

int EndFlag=1;		//会話が終わったか
int KeyWaitFlag=0;	//ボタン入力待ち
int WaitCount=0;
 
bool isLeadShiftJIS(unsigned char c)
{
    return (c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFC);
}
 
void setMessage(char *pMsg)
{
	KeyWaitFlag = 0 ;
	EndFlag = 0 ;
	WaitCount = 0 ;
    // ログをクリア
    for (int i=0; i<LINES; i++) {
        MessageLog[i][0] = '\0';
    }
    // 先頭行から書き込む
    messagelog_line_index = 0;
    // 加工前テキストのポインタ記憶
    pMessage = pMsg;
}
 
void Log_LineBreak(void)
{
	messagelog_line_index ++;
	if (messagelog_line_index >= LINES) {
		messagelog_line_index = 0;
		// ログをクリア
		for (int i=0; i<LINES; i++) {
			MessageLog[i][0] = '\0';
		}
	}
	MessageLog[messagelog_line_index][0] = '\0';
}
 
void Log_Add(char *str ) // ※手抜き
{
	int len = strlen(MessageLog[messagelog_line_index]) + strlen(str);
	//右端まで書いたら改行
	if (len > COLUMNS) {
		Log_LineBreak();
	}
	//ログにstrを連結
	strcat_s(MessageLog[messagelog_line_index], str);
}
 
void Talk_Proc(InputCount_t *InputCount)
{
	if (pMessage == NULL) return;

	char str[3];
	while (str[0] = *pMessage)
	{
		if(KeyWaitFlag==0){
			pMessage++;
		}
 
        if (str[0] == '@') {
            // 改行
            Log_LineBreak();
            continue; // 継続
        }
		if (str[0] == 'B') {
			//ボタン入力待ち
			KeyWaitFlag = 1 ;
			continue; // 継続
		}
		if (str[0] == 'C') {
			messagelog_line_index = 0;
			// ログをクリア
			for (int i=0; i<LINES; i++) {
				MessageLog[i][0] = '\0';
			}
			MessageLog[messagelog_line_index][0] = '\0';
			continue ;
		}
		if (str[0] == 'E') {
			//終わり
			EndFlag = 1 ;
			break ;
		}
		//入力待ちフラグが0のときだけ文章を進める
		if(KeyWaitFlag==0){
			if (isLeadShiftJIS(str[0])) {
				// シフトJISの1バイト目のとき
				str[1] = *(pMessage++);
				str[2] = '\0';
			}
			else {
				str[1] = '\0';
        }
			Log_Add(str);
		}
		//入力待ち中は▼用カウントを増加
		if(KeyWaitFlag==1){
			WaitCount++;
		}
		//入力待ちの時、Aボタンを押すと文章が進んで▼用カウントが0
		if(KeyWaitFlag==1 && InputCount->A==1){
			KeyWaitFlag = 0 ;
			WaitCount = 0 ;
		}
		break;

	}
	
}
 
void Talk_Draw(void)
{

	if(EndFlag==0){
		//メッセージボックス描画
		DrawGraph( 20, 292, Made1, TRUE );
		DrawGraph( 0, 288, MessageBoxImg, TRUE );

		//文字を表示
		for (int i=0; i<LINES; i++) {
			DrawStringToHandle(128, 296+FONT_SIZE * i, MessageLog[i], GetColor(255, 255, 255),FontHandle );
		}
		//ボタン入力待ちの時、30フレームごとに▼を点滅
		if(KeyWaitFlag==1 && WaitCount%60<=30){
			DrawStringToHandle(464, 352, "▼", GetColor(255, 255, 255),SankakuFont );
		}
	}
}

//初期化処理
void Talk_Initialize()
{

        //メッセージボックス
        MessageBoxImg = LoadGraph( "Image/Frame/TalkFrame.png" );
		Made1 = LoadGraph( "Image/Face/Made1.png" );
		FontHandle = CreateFontToHandle("メイリオ" , 21 , 2 , DX_FONTTYPE_ANTIALIASING);
		SankakuFont = CreateFontToHandle("メイリオ" , 30 , 2 , DX_FONTTYPE_ANTIALIASING);
}

void Message(){
	//F1 F2を押すとメッセージをセットする
	//F3を押すと空の文字列をセットする

	if( CheckHitKey( KEY_INPUT_F1 ) ) {
		setMessage("ボタン押し待ち改行B @"
			"ボタン押し待ちBボタン押し待ちクリアボタン押し待ちクリアB C"
			"ボタン押し待ちB自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行ボタン押し待ちエンドB E");
	}else if( CheckHitKey( KEY_INPUT_F2 ) ) {
		setMessage("ボタン押し待ちエンドB E");
	}else if( CheckHitKey( KEY_INPUT_F3 ) ) {
		setMessage("自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行"
			"自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動エンドE");
	}


}


アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: サウンドノベル風の文章表示

#22

投稿記事 by softya(ソフト屋) » 8年前

そこまで頼ってしまうと今後の力がつかないと思いますので、まず自分で調べて見ませんか?
どちらにても今後の拡張も必要になるのでサンプルはサンプル。自分で理解することが大事だと私は思います。
それに、今後サンプルが探せない難しい部分の開発に入ることになると思いますので、なんても聞いてしまうと力を付ける機会を失います。
どうしても調べまくっても分からない部分があれば聞いてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Chalaza

Re: サウンドノベル風の文章表示

#23

投稿記事 by Chalaza » 8年前

すこし付け加えただけでできました。よく理解しないで聞いてばっかりですみませんでした。
順に追って構造をちゃんと理解しないと駄目ですね。
綺麗に動くようになったので無駄があると思いますががこれで一応解決にさせていただきます。

コード:

#include "DxLib.h"
#include <string.h>
#include "Keyboard.h"
 
#define FONT_SIZE 20
#define LINES     4
#define COLUMNS   46
 
// メッセージ表示用テキストのバッファ
char MessageLog[LINES][COLUMNS+1];
// 書き込み対象の行インデックス
int messagelog_line_index;
 
// 加工前テキストの加工位置を指すポインタ
char *pMessage;

int MessageBoxImg;	//メッセージボックス画像
int Made1;			//顔グラ
int FontHandle;		//フォントハンドル
int SankakuFont;	//▼のフォントハンドル

int EndFlag=1;		//会話が終わったか
int KeyWaitFlag=0;	//ボタン入力待ち
int WaitCount=0;
 
bool isLeadShiftJIS(unsigned char c)
{
    return (c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFC);
}
 
void setMessage(char *pMsg)
{
	KeyWaitFlag = 0 ;
	EndFlag = 0 ;
	WaitCount = 0 ;
    // ログをクリア
    for (int i=0; i<LINES; i++) {
        MessageLog[i][0] = '\0';
    }
    // 先頭行から書き込む
    messagelog_line_index = 0;
    // 加工前テキストのポインタ記憶
    pMessage = pMsg;
}
 
void Log_LineBreak(void)
{
	messagelog_line_index ++;
	if (messagelog_line_index >= LINES) {
		messagelog_line_index = 0;
		// ログをクリア
		for (int i=0; i<LINES; i++) {
			MessageLog[i][0] = '\0';
		}
	}
	MessageLog[messagelog_line_index][0] = '\0';
}
 
void Log_Add(char *str ) // ※手抜き
{
	int len = strlen(MessageLog[messagelog_line_index]) + strlen(str);
	//右端まで書いたら改行
	if (len > COLUMNS) {
		Log_LineBreak();
	}
	//ログにstrを連結
	strcat_s(MessageLog[messagelog_line_index], str);
}
 
void Talk_Proc(InputCount_t *InputCount)
{
	if (pMessage == NULL) return;

	char str[3];
	while (str[0] = *pMessage)
	{
		//入力待ちでないとき
		if(KeyWaitFlag==0){
			pMessage++;
		}
 
        if (str[0] == '@') {
            // 改行
            Log_LineBreak();
            continue; // 継続
        }
		if (str[0] == 'B') {
			str[0] = '\0';	//現在のBを削除
			pMessage--;
			//ボタン入力待ち
			KeyWaitFlag = 1 ;
			continue; // 継続
		}
		if (str[0] == 'C') {
			messagelog_line_index = 0;
			// ログをクリア
			for (int i=0; i<LINES; i++) {
				MessageLog[i][0] = '\0';
			}
			MessageLog[messagelog_line_index][0] = '\0';
			continue ;
		}
		if (str[0] == 'E') {
			//終わり
			EndFlag = 1 ;
			continue ;
		}
		//入力待ちフラグが0のときだけ文章を進める
		if(KeyWaitFlag==0){
			if (isLeadShiftJIS(str[0])) {
				// シフトJISの1バイト目のとき
				str[1] = *(pMessage++);
				str[2] = '\0';
			}
			else {
				str[1] = '\0';
        }
			Log_Add(str);
		}
		//入力待ち中は▼用カウントを増加
		if(KeyWaitFlag==1){
			WaitCount++;
		}
		//入力待ちの時、Aボタンを押すと文章が進んで▼用カウントが0
		if(KeyWaitFlag==1 && InputCount->A==1){
			KeyWaitFlag = 0 ;
			WaitCount = 0 ;
			pMessage+=2;
		}
		break;

	}
	
}
 
void Talk_Draw(void)
{

	if(EndFlag==0){
		//メッセージボックス描画
		DrawGraph( 20, 292, Made1, TRUE );
		DrawGraph( 0, 288, MessageBoxImg, TRUE );

		//文字を表示
		for (int i=0; i<LINES; i++) {
			DrawStringToHandle(128, 296+FONT_SIZE * i, MessageLog[i], GetColor(255, 255, 255),FontHandle );
		}
		//ボタン入力待ちの時、30フレームごとに▼を点滅
		if(KeyWaitFlag==1 && WaitCount%60<=30){
			DrawStringToHandle(464, 352, "▼", GetColor(255, 255, 255),SankakuFont );
		}
	}
}

//初期化処理
void Talk_Initialize()
{

        //メッセージボックス
        MessageBoxImg = LoadGraph( "Image/Frame/TalkFrame.png" );
		Made1 = LoadGraph( "Image/Face/Made1.png" );
		FontHandle = CreateFontToHandle("メイリオ" , 21 , 2 , DX_FONTTYPE_ANTIALIASING);
		SankakuFont = CreateFontToHandle("メイリオ" , 30 , 2 , DX_FONTTYPE_ANTIALIASING);
}

void Message(){
	//F1 F2を押すとメッセージをセットする
	//F3を押すと空の文字列をセットする

	if( CheckHitKey( KEY_INPUT_F1 ) ) {
		setMessage("ボタン押し待ち改行B@"
			"ボタン押し待ちBボタン押し待ちクリアボタン押し待ちクリアBC"
			"ボタン押し待ちB自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行ボタン押し待ちエンドBE");
	}else if( CheckHitKey( KEY_INPUT_F2 ) ) {
		setMessage("ボタン押し待ちエンドB E");
	}else if( CheckHitKey( KEY_INPUT_F3 ) ) {
		setMessage("自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行"
			"自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動改行自動エンドE");
	}


}


ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#24

投稿記事 by ISLe » 8年前

'B'のときcontinueじゃなくてbreakするだけで良かったんじゃないですかね。

入力待ちの処理は文字切り出しループの中に入れないほうが分かりやすいと思います。
変なふうにポインタ弄らなくて済みますし。

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: サウンドノベル風の文章表示

#25

投稿記事 by ISLe » 8年前

せっかくなのでまた単体で動作するサンプルプログラムを作ってみました。

コード:

#include "DxLib.h"
#include <string.h>

#define FONT_SIZE 20
#define LINES     4
#define COLUMNS   46

// メッセージ表示用テキストのバッファ
char MessageLog[LINES][COLUMNS+1];
// 書き込み対象の行インデックス
int messagelog_line_index;

// 加工前テキストの加工位置を指すポインタ
char *pMessage;

// 停止フラグ
bool bStop = false;
// 待ちフラグ
bool bWait = false;
// 待ちカーソル点滅用カウンタ
int cWait;

bool isLeadShiftJIS(unsigned char c)
{
	return (c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFC);
}

void Log_Clear(void)
{
	// ログをクリア
	for (int i=0; i<LINES; i++) {
		MessageLog[i][0] = '\0';
	}
	// 先頭行から書き込む
	messagelog_line_index = 0;
}

void Log_LineBreak(void)
{
	messagelog_line_index ++;
	if (messagelog_line_index >= LINES) {
		Log_Clear();
	}
	MessageLog[messagelog_line_index][0] = '\0';
}

void Log_Add(char *str) // ※手抜き
{
	int len = strlen(MessageLog[messagelog_line_index]) + strlen(str);
	if (len > COLUMNS) {
		Log_LineBreak();
	}
	strcat(MessageLog[messagelog_line_index], str);
}

void Talk_Proc(void)
{
	if (pMessage == NULL) return;
	if (bStop) return;

	if (bWait) {
		cWait++;
		cWait %= 60;
		return;
	}

	char str[3];
	while (str[0] = *pMessage)
	{
		pMessage++;

		if (str[0] == '@') {
			// 改行
			Log_LineBreak();
			continue;
		}
		if (str[0] == 'B') {
			// 入力待ち
			bWait = true;
			cWait = 0;
			break;
		}
		if (str[0] == 'C') {
			// クリア
			Log_Clear();
			continue;
		}
		if (str[0] == 'E') {
			// 終わり
			bStop = true;
			break;
		}

		if (isLeadShiftJIS(str[0])) {
			// シフトJISの1バイト目のとき
			str[1] = *(pMessage++);
			str[2] = '\0';
		}
		else {
			str[1] = '\0';
		}
		Log_Add(str);
		break;
	}
}

void Talk_Draw(void)
{
	if (bStop) return;

	DrawBox(10-2, 10-2, FONT_SIZE/2*COLUMNS+10+2, FONT_SIZE*LINES+10+2, GetColor(255, 255, 255), FALSE);
	for (int i=0; i<LINES; i++) {
		DrawString(10, 10 + FONT_SIZE * i, MessageLog[i], GetColor(255, 255, 255));
	}
	if (bWait && cWait < 30) {
		DrawString(10, FONT_SIZE*LINES+10+4, "▼", GetColor(255, 255, 255));
	}
}

void setMessage(char *pMsg)
{
	Log_Clear();
	// 加工前テキストのポインタ記憶
	pMessage = pMsg;
	// フラグをリセット
	bStop = bWait = false;
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	ChangeWindowMode(TRUE);
	if (DxLib_Init() != 0) return 0;
	SetDrawScreen(DX_SCREEN_BACK);
	SetFontThickness(0); // ※太いと枠をはみ出すので
	SetFontSize(FONT_SIZE);
	while (ProcessMessage() == 0 && ClearDrawScreen() == 0)
	{
		if (CheckHitKey(KEY_INPUT_RETURN)) {
			bWait = false;
		}
		if (CheckHitKey(KEY_INPUT_F1)) {
			setMessage(
				"ボタン押し待ち改行B@"
				"ボタン押し待ちBボタン押し待ちクリアボタン押し待ちクリアBC"
				"ボタン押し待ちB自動改行自動改行自動改行自動改行自動改行"
				"自動改行自動改行自動改行自動改行ボタン押し待ちエンドBE");
		} else if (CheckHitKey(KEY_INPUT_F2)) {
			setMessage("ボタン押し待ちエンドBE");
		} else if (CheckHitKey(KEY_INPUT_F3)) {
			setMessage(
				"自動改行自動改行自動改行自動改行自動改行自動改行自動改行"
				"自動改行自動改行自動改行自動改行自動改行自動改行自動改行"
				"自動改行自動改行自動改行自動改行自動エンドE");
		}
		Talk_Proc();
		Talk_Draw();
		ScreenFlip();
	}
	DxLib_End();
	return 0;
}

閉鎖

“C言語何でも質問掲示板” へ戻る