ソースファイル一式 source code
06-FSAct2.zip
MITライセンスです。
This software/source is licensed under the MIT License, see LICENSE.txt.
FSAct2.cs
//	#6 固定画面アクション2 FS Action2 2017/11/27 T.Umezawa
using System;
using System.Collections.Generic;
class Util
{
	public static int GetAngle4i( double dx, double dy )
	{
		return( (int)( ( GetAngle360( dx, dy ) + 45 ) / 90 ) & 0x03 );
	}
	public static double GetAngle360( double dx, double dy )
	{
		double	r = Math.Atan2( dy, dx ) * 180 / Math.PI;
		if( r < 0 ){
			r += 360;
		}
		return( r );
	}
}
class Map
{
	static System.Drawing.Bitmap[]	sBM = {
		new System.Drawing.Bitmap( "block.png" ),
		new System.Drawing.Bitmap( "flag.png" ),
	};
	public static byte[,]		sMap = new byte[ 23, 30 ];
	public static void create()
	{
		for( int x = 0; x < sMap.GetLength( 1 ); x++ ){
			sMap[ sMap.GetLength( 0 ) - 1, x ] = 1;
			sMap[ sMap.GetLength( 0 ) - 2, x ] = 1;
		}
		byte	v = 1;
		int		n = 1;
		for( int y = 6; y <= 18; y += 3 ){
			for( int x = 0; x = 0 && x < sMap.GetLength( 1 ) &&
		        y >= 0 && y < sMap.GetLength( 0 ) );
	}
	public static bool IsBlock( float x, float y )
	{
		x -= 0.5f;
		y -= 0.5f;
		int		x0 = (int)Math.Floor( x );
		int		y0 = (int)Math.Floor( y );
		int		x1 = (int)Math.Ceiling( x );
		int		y1 = (int)Math.Ceiling( y );
		return( IsBlock( x0, y0 ) ||
		        IsBlock( x1, y0 ) ||
		        IsBlock( x0, y1 ) ||
		        IsBlock( x1, y1 ) );
	}
	public static bool IsBlock( int x, int y )
	{
		return( !IsArea( x, y ) || sMap[ y, x ] == 1 );
	}
}
class Unit
{
	public static readonly int		JUMP = 8;
	public static readonly int		DOT = 8;
	public static readonly int		DTH = DOT / 2;
	public float	mX, mY;
	public float	mDX, mDY;
	protected int	mJump;
	public Unit( float x, float y, float dx, float dy )
	{
		mX = x;
		mY = y;
		mDX = dx;
		mDY = dy;
	}
	public int getAngle4i( Unit u )
	{
		return( Util.GetAngle4i( u.mX - mX, u.mY - mY ) );
	}
	public bool isBlock()
	{
		return( Map.IsBlock( mX, mY ) );
	}
	public void jump( float dy )
	{
		if( mJump == 0 ){
			mJump = 1;
		}
		mDY = dy;
	}
	public virtual void step()
	{
		if( mJump > 0 ){
			mJump++;
		}
		mX += mDX;
		if( isBlock() ){
			mDX = -mDX;
			mX += mDX;
		}
		mY += mDY;
		if( mJump > 0 && mDY < 6.0f / DOT ){	//	重力
			mDY += 1.0f / 32;
		}
		if( mDY < 0 && isBlock() ){			//	上昇中
			if( !Map.IsBlock( mX + 1.0f / 16, mY ) ){
				mX += 1.0f / 16;
			}else if( !Map.IsBlock( mX - 1.0f / 16, mY ) ){
				mX -= 1.0f / 16;
			}else{							//	天井にぶつかる
				mDY = 0;
				mY = (int)( mY - 0.5f ) + 1.5f;
			}
		}
		if( mDY > 0 && isBlock() ){			//	下降中
			mDY = 0;						//	着地
			mY = (int)( mY - 0.5f ) + 0.5f;
			mJump = 0;
		}
		if( mJump == 0 && !Map.IsBlock( mX, mY + 1.0f / DOT ) ){	//	地面から落ちる
			mJump = JUMP;
			mY += 1.0f / DOT;
			mDY = 1.0f / 16;
		}
	}
	public bool isCollision( Unit u )
	{
		return( Math.Abs( mX - u.mX ) < 6.0f / DOT && Math.Abs( mY - u.mY ) < 6.0f / DOT );
	}
}
class Player : Unit
{
	static System.Drawing.Bitmap[]	sBM = {
		new System.Drawing.Bitmap( "player.png" ),
		new System.Drawing.Bitmap( "player2.png" )
	};
	static System.Drawing.Rectangle	sRect = new System.Drawing.Rectangle( 0, 0, 8, 8 );
	public int		mType;
	public int		mBtn;
	public Player( int type ) : base( 0.5f, 20.5f, 1.0f / 16, 0 )
	{
		mType = type;
	}
	public void draw( System.Drawing.Graphics g )
	{
		sRect.X = ( (int)( mX * 8 ) & 1 ) * DOT;
		sRect.Y = Math.Sign( mDX ) * DTH + DTH;
		if( mJump != 0 ){
			sRect.X = 0;
		}
		g.DrawImage( sBM[ mType ], mX * DOT - DTH, mY * DOT - DTH, sRect, System.Drawing.GraphicsUnit.Pixel );
	}
	public void jump()
	{
		if( mJump < JUMP ){			//	接地中又は離陸時の場合
			jump( -5.0f / 16 );
		}
	}
	public void reverse()
	{
		mDX = -mDX;
	}
	public override void step()
	{
		if( mBtn > 0 ){
			jump();
		}
		base.step();
		if( mX <= 0.5f || mX >= Map.sMap.GetLength( 1 ) - 0.5f ){
			mDX = Math.Sign( Map.sMap.GetLength( 1 ) / 2 - mX ) / 16.0f;
		}
		if( Map.sMap[ (int)mY, (int)mX ] == 2 ){
			FSAct2.sGameClear = true;
		}
	}
}
class Enemy : Unit
{
	public static List			sList;
	static System.Drawing.RectangleF	sRectD = new System.Drawing.RectangleF( 0, 0, 8, 8 );
	static System.Drawing.RectangleF	sRectS = new System.Drawing.RectangleF( 0, 0, 8, 8 );
	static System.Drawing.Bitmap[]	sBM = {
		new System.Drawing.Bitmap( "monster.png" ),
		new System.Drawing.Bitmap( "monster2.png" ),
	};
	public int		mType;
	public int		mState;
	public float Speed{	get{	return( 1.0f / 32 * ( mType + 1 ) * ( mState + 1 ) );	}	}
	public Enemy( int type ) : base( FSAct2.sRnd.Next( Map.sMap.GetLength( 1 ) - 2 ) + 1, 0.5f, ( FSAct2.sRnd.Next( 2 ) / 16.0f - 1.0f / 32 ) * ( type + 1 ), 0 )
	{
		mType = type;
	}
	public void draw( System.Drawing.Graphics g )
	{
		sRectD.Y = mY * DOT - DTH;
		if( mDX < 0 ){
			sRectD.X = mX * DOT - DTH;
			sRectD.Width = 8;
		}else{
			sRectD.X = mX * DOT + DTH;
			sRectD.Width = -8;
		}
		sRectS.X = mState * DOT;
		g.DrawImage( sBM[ mType ], sRectD, sRectS, System.Drawing.GraphicsUnit.Pixel );
	}
	public void kill()
	{
		sList.Add( new Enemy( mType ) );
		sList.Remove( this );
	}
	public void step( List le )
	{
		step();
		if( mX <= 0.5f || mX >= Map.sMap.GetLength( 1 ) - 0.5f ){
			mDX = Math.Sign( Map.sMap.GetLength( 1 ) / 2 - mX ) * Speed;
		}
		foreach( Enemy en in le ){
			if( en == this || !isCollision( en ) ){
				continue;
			}
			bool	km = ( mState == 1 && mDX != 0 );
			bool	ke = ( en.mState == 1 && en.mDX != 0 );
			if( km )	en.kill();
			if( ke )	kill();
			if( km || ke ){
				continue;
			}
			mDX = Math.Sign( mX - en.mX ) * Speed;
			if( mDX == 0 ){
				mDX = Speed;
			}
			en.mDX = -mDX;
		}
	}
}
class FSAct2 : MyForm
{
	public static Random		sRnd = new Random();
	System.Drawing.Font			mFont = new System.Drawing.Font( "MS Gothic", 5 );
	int							mCount;
	List				mLPlayer = new List();
	public static bool			sGameClear, sGameOver;
	int							mStage = 1;
	int							mScene;
	protected override void OnLoad( EventArgs e )
	{
		base.OnLoad( e );
		mTimer.Interval = 33;
		mTimer.Start();
	}
	protected override void onMyPaint( System.Drawing.Graphics g )
	{
		if( mScene == 0 ){
			g.DrawString( "固定画面アクション2 FS Action2", mFont, mSBWhite, 60, 30 );
			g.DrawString( "PRESS ANY KEY", mFont, mSBWhite, 90, 90 );
			return;
		}
		Map.draw( g );
		foreach( Player pl in mLPlayer ){
			pl.draw( g );
		}
		foreach( Enemy en in Enemy.sList ){
			en.draw( g );
		}
		g.DrawString( "TIME " + mCount, mFont, mSBWhite, 0, 0 );
		g.DrawString( "STAGE " + mStage, mFont, mSBWhite, 40, 0 );
		if( sGameClear ){
			g.DrawString( "STAGE CLEAR!", mFont, mSBWhite, 90, 90 );
		}
		if( sGameOver ){
			g.DrawString( "GAME OVER", mFont, mSBWhite, 90, 90 );
		}
	}
	protected override void onMyTimer( object sender, System.Timers.ElapsedEventArgs e )
	{
		if( MouseMiddle == 1 )	input( 0, true  );
		if( MouseLeft   >  0 )	input( 0, false );
		if( MouseLeft   == 0 )	release( 0 );
		if( MouseRight  == 1 ){	Player p = getPlayer( 0 );	if( p != null )	p.reverse();	}
		if( mKey[ (int)System.Windows.Forms.Keys.R ] == 1 )	input( 1, true  );
		if( mKey[ (int)System.Windows.Forms.Keys.Z ] >  0 )	input( 1, false );
		if( mKey[ (int)System.Windows.Forms.Keys.Z ] == 0 )	release( 1 );
		if( mKey[ (int)System.Windows.Forms.Keys.X ] == 1 ){	Player p = getPlayer( 1 );	if( p != null )	p.reverse();	}
		if( sGameClear || sGameOver ){
			return;
		}
		mCount++;
		foreach( Player pl in mLPlayer ){
			pl.step();
			for( int i = Enemy.sList.Count - 1; i >= 0; i-- ){
				Enemy	en = Enemy.sList[ i ];
				if( !pl.isCollision( en ) ){	//	敵に接触
					continue;
				}
				if( pl.getAngle4i( en ) != 1 ){	//	敵にやられる
					sGameOver = true;
					continue;
				}
				pl.jump( -4.0f / 16 );
				if( en.mType == 0 ){	//	スライムだった場合
					en.kill();
					continue;
				}
				if( en.mState == 0 ){
					en.mState = 1;
					en.mDX = 0;
					continue;
				}
				en.mDX = Math.Sign( en.mX - pl.mX );
				if( en.mDX == 0 ){
					en.mDX = 1;
				}
				en.mDX *= en.Speed;
			}
		}
		for( int i = Enemy.sList.Count - 1; i >= 0; i-- ){
			Enemy	en = Enemy.sList[ i ];
			en.step( Enemy.sList );
		}
		base.onMyTimer( sender, e );
		Invalidate();
	}
	void input( int type, bool res )
	{
		if( mScene == 0 ){
			mStage = 1;
			start();
			mLPlayer.Add( new Player( type ) );
		}else if( sGameClear ){
			mStage++;
			start();
			mLPlayer.Add( new Player( type ) );
		}else if( res ){
			mStage = 1;
			start();
			mLPlayer.Add( new Player( type ) );
		}else if( mLPlayer[ 0 ].mType != type ){
			if( mLPlayer.Count == 1 ){
				mLPlayer.Add( new Player( type ) );
			}else{
				mLPlayer[ 1 ].mBtn = 1;
			}
		}else{
			mLPlayer[ 0 ].mBtn = 1;
		}
	}
	Player getPlayer( int type )
	{
		foreach( Player p in mLPlayer ){
			if( p.mType == type ){
				return( p );
			}
		}
		return( null );
	}
	void release( int type )
	{
		Player	p = getPlayer( type );
		if( p != null ){
			p.mBtn = 0;
		}
	}
	void start()
	{
		mScene = 1;
		sGameClear = false;
		sGameOver = false;
		mCount = 0;
		mLPlayer.Clear();
		Enemy.sList = new List();
		for( int i = 0; i < mStage * 2; i++ ){
			Enemy.sList.Add( new Enemy( 0 ) );
			Enemy.sList.Add( new Enemy( 1 ) );
		}
		Map.create();
	}
	[STAThread]
	static void Main()
	{
		System.Windows.Forms.Application.Run( new FSAct2() );
	}
}
      
MyForm.cs
//	Form継承 2017/11/27 T.Umezawa
using System;
using System.Collections.Generic;
class MyForm : System.Windows.Forms.Form
{
	protected System.Timers.Timer		mTimer = new System.Timers.Timer();
	protected System.Drawing.SolidBrush	mSBWhite = new System.Drawing.SolidBrush( System.Drawing.Color.White );
	public int[]		mKey = new int[ 0x100 ];
	public int[]		mMouseB = new int[ 0x20 ];
	public int MouseLeft{	get{	return( mMouseB[ (int)Math.Log( (int)System.Windows.Forms.MouseButtons.Left  , 2 ) ] );	}	}
	public int MouseMiddle{	get{	return( mMouseB[ (int)Math.Log( (int)System.Windows.Forms.MouseButtons.Middle, 2 ) ] );	}	}
	public int MouseRight{	get{	return( mMouseB[ (int)Math.Log( (int)System.Windows.Forms.MouseButtons.Right , 2 ) ] );	}	}
	protected override void OnKeyDown( System.Windows.Forms.KeyEventArgs e )
	{
		mKey[ (int)e.KeyCode ] = 1;
		base.OnKeyDown( e );
	}
	protected override void OnKeyUp( System.Windows.Forms.KeyEventArgs e )
	{
		mKey[ (int)e.KeyCode ] = 0;
		base.OnKeyUp( e );
	}
	protected override void OnMouseDown( System.Windows.Forms.MouseEventArgs e )
	{
		mMouseB[ (int)Math.Log( (int)e.Button, 2 ) ] = 1;
		base.OnMouseDown( e );
	}
	protected override void OnMouseUp( System.Windows.Forms.MouseEventArgs e )
	{
		mMouseB[ (int)Math.Log( (int)e.Button, 2 ) ] = 0;
		base.OnMouseUp( e );
	}
	protected override void OnLoad( EventArgs e )
	{
		ClientSize = new System.Drawing.Size( 960, 720 );
		Left = 396;	//	キャプチャ都合上
		Top = 20;	//	キャプチャ都合上
		DoubleBuffered = true;
		BackColor = System.Drawing.Color.FromArgb( 0x55, 0x88, 0xff );
		mTimer.Elapsed += new System.Timers.ElapsedEventHandler( onMyTimer );
	}
	protected override void OnPaint( System.Windows.Forms.PaintEventArgs e )
	{
		base.OnPaint( e );
		System.Drawing.Graphics	g = e.Graphics;
		g.ScaleTransform( 4, 4 );
		g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
		g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
		onMyPaint( g );
	}
	protected virtual void onMyPaint( System.Drawing.Graphics g )
	{
	}
	protected virtual void onMyTimer( object sender, System.Timers.ElapsedEventArgs e )
	{
		for( int i = 0; i < mKey.Length; i++ ){
			if( mKey[ i ] > 0 ){
				mKey[ i ]++;
			}
		}
		for( int i = 0; i < mMouseB.Length; i++ ){
			if( mMouseB[ i ] > 0 ){
				mMouseB[ i ]++;
			}
		}
	}
}