※点(星)が移動するだけです。以下、SF。
1982年 初代PC-9801用ゲーム、「VALIANT」でSFを見る。- 当時では前代未聞の驚異的なCG。伝説の天才プログラマ、マークフリント作。
- C言語は存在してたものの、まだ使用できるコンパイラは皆無で事実上、BASICかASMの二択。BASICでは速度出ないので当然ASM。
- 三角比をルックアップテーブルとして用意、参照しているか、ALU駆使の自作のライブラリを使用していかのどちらかと推測。
- x86はMUL、DIV命令はあるがsin,cos等は実装さておらず、それらはi8087 浮動小数数値演算コ・プロセッサ(当時8万円位する)を入れないとH.W.(ASM)で計算不可。
- i8087にしてもNAND回路等で超越関数を設計するなんてこれまた凄い話。
1989年 Xが動くようになったので同僚がSFを作る。自分も試みたができず。
- プラットフォーム SVR4,X11R4,Xlib(C言語)
- 完成させた記憶は無く、職務で作る訳でもないので放置したと思う。
(これ以前にDOS版Turbo Cで作りかけた覚えはあるが定かでない)
2024年 35年ぶりに作ってみる。
- プラットフォーム WPF(C#),SkiaSharp
- 特に難しい事はやってません。とても簡単なコードです。
- 配列データを順次シフトさせて表示してます。
動画1. 順方向(27インチ WQHD 2560x1440,自作機 i5-7600K 3.80GHz)
動画2. 逆方向
特徴
- 星は色が違うので4パターン用意してランダムに可変。大きさも複数用意。
- 最初に表示する星の大きさ、拡大比率等はコード先頭のconst値で可変。
- 画面中央に表示させて眺めていると段々眠くなってくる催眠効果(?)も持ち合わせてます。
1.キー操作説明
- [PageUp,Dw]........速度可変
- [カーソル上下]......星の数可変
- [マウス右]...............表示を中央固定とマウス追従の切り替え
- [マウス左]...............終了
2.全体的な流れ
1.SfMain()でSTAR_ANGLE[0,h]にどちらの方向に移動するかの角度を入れる
2.SfPaintSurface()全ての星表示
3.画面消す
4.DataShift()でデータを[0]->[1]、[1]->[2]、[2]->[3]...にシフト
5. 1に戻る
これらを連続的に繰り返す事により星が移動している様に見える
3.星のXY値の算出
STAR_ANGLE配列に入っている角度から順次XY値を計算してその位置にCircle()で描く。
ttは中心部からの半径増加分。
X=(float)(this.Width /2 + (tt+L)* Math.Sin (STAR_ANGLE[tt,h] * Math.PI/180) );
Y=(float)(this.Height/2 - (tt+L)* Math.Cos (STAR_ANGLE[tt,h] * Math.PI/180) );
4.WPF (C#) 純方向版(前から後へ)
2パターン用意。動く方向が違うだけでコードは殆ど同じです。
/////////////////////////////////////////
// STARFIELD (NORMAL) WPF+SKiaSharp.
// (c)inf102 S.H 2024.
// Use PageUp,Dw / CursolUp,Dw Key.
/////////////////////////////////////////
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace SF {
public partial class MainWindow : Window {
// 速度、星数の表示
static int PARAM_SIZE=0;
static bool PARAM_SIZE_FLG; // 設定値表示中に変更キーを押した際のキャンセル用
static bool OP_FLG; // OP_Message表示フラグ
// マウス制御
static bool MOUSE_FLG;
// マウス位置
static int MX,MY;
// 描画を始めてから外枠までの繰返しのカウンタ FIELD_MAXまでaddされる [0固定]
static int FIELD_CNT=0;
////////////////// [ ]内は設定参考値 /////////////////////////
// 1フィードに表示する最大星数。STARより大きい必要がある。起動中にSTARを可変させるので最大値を最初に設定する必要がある。
const int STAR_MAX = 30; // [50]
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int STAR=2; // 1フィールド中の星描画数 1~STAR_MAX [3]
static int WAIT=39; // [35]
const int CENTER_SPACE = 15; // 星が最初に表示される空白エリアの広さ [15]
const int FIELD_MAX = 83; // 最大広がりエリア。画面端に星が来ない場合は大きくする。
// 中心部を画面中央にして70で画面いっぱいに表示されたとしても表示位置を端にすると反対には表示
// されない事がある。その場合も数値を上げるが大きすぎると余計な計算が増える。 [83]
const float FIELD_RATE =1.060f; // 拡大割合。WAIT後にどれだけ接近するか。大きいとカクカクになる。 [1.060f]
static int[,] STAR_ANGLE = new int[FIELD_MAX, STAR_MAX]; // 星が移動する角度
// 最初に表示する星の大きさ
const float No1_DEFAULT_SIZE = 1.32f; // [1.05f]
const float No2_DEFAULT_SIZE = 1.05f; // [1.05f]
const float No3_DEFAULT_SIZE = 1.74f; // [1.74f]
const float No4_DEFAULT_SIZE = 2.35f; // [2.40f]
// 星の拡大率
const float No1_ZOOM_RATE = 1.0093f; // [1.0093f]
const float No2_ZOOM_RATE = 1.0299f; // [1.0299f]
const float No3_ZOOM_RATE = 1.0069f; // [1.0069f]
const float No4_ZOOM_RATE = 1.0085f; // [1.0215f]
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public MainWindow() {
InitializeComponent();
}
// 配列データシフト
public void DataShift(){
int []bk1 = new int [STAR_MAX];
int []bk2 = new int [STAR_MAX];
for ( int x=0;x<STAR;x++ ) bk1[x]=STAR_ANGLE[0,x];
int flg=0;
for ( int x=1;x<FIELD_MAX;x++ ){
if ( flg == 0 ){
for ( int tm=0; tm<STAR; tm++ ){
bk2[tm] = STAR_ANGLE[x,tm];
STAR_ANGLE[x,tm] = bk1[tm];
}
flg=1;
continue;
}
for ( int tm=0; tm<STAR; tm++ ){
bk1[tm] = STAR_ANGLE[x,tm];
STAR_ANGLE[x,tm] = bk2[tm];
}
flg=0;
}
}
private async void Window_Loaded(object sender, RoutedEventArgs e) {
// 非同期メインループ
_= Task.Run(() => SfMain());
// OP Msg
OP_FLG = true;
await Task.Run( async ()=>{
for (int ex=1;ex<130;ex++){
PARAM_SIZE=ex;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
await Task.Delay(8);
}
// 表示を消す
PARAM_SIZE=999;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
});
OP_FLG = false;
}
// 非同期メインループ
void SfMain(){
Random rnd = new Random();
while (true){
// 星が移動する方向(角度)設定 (新たな星の生成)
for ( int h=0;h<STAR;h++ ){
STAR_ANGLE[0,h]=rnd.Next(0, 360);
}
// 星表示
this.Dispatcher.Invoke ((Action)(() =>{
Sf.InvalidateVisual();
}));
// 星の配列データをシフト
DataShift();
// 何処まで展開するか
FIELD_CNT++;
if (FIELD_CNT >= FIELD_MAX) FIELD_CNT=FIELD_MAX;
Thread.Sleep(WAIT);
}
}
// マウスの位置を星の中心位置にする
private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) {
System.Windows.Point point = e.GetPosition(this);
MX=(int)point.X;
MY=(int)point.Y;
}
// マウス左ボタン アプリシャットダウン
private void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
Application.Current.Shutdown();
}
// マウス右ボタン 表示位置をマウスに追従させるか
private void Window_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
MOUSE_FLG=!MOUSE_FLG;
}
// 1フィールド中の星数可変カーソル↑↓ , 速度可変 PageUp,Dw
private async void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) {
if ( (!(e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
&& !(e.Key == System.Windows.Input.Key.PageUp || e.Key == System.Windows.Input.Key.PageDown)) ) return;
// 星数
if ( e.Key == System.Windows.Input.Key.Up && (STAR < STAR_MAX) ) STAR++;
if ( e.Key == System.Windows.Input.Key.Down && (STAR >= 2) ) STAR--;
// 速度
if ( e.Key == System.Windows.Input.Key.PageUp ) WAIT-=3;
if ( WAIT <=0 ) WAIT=2;
if ( e.Key == System.Windows.Input.Key.PageDown ) WAIT+=3;
// 設定値を画面に出す
if (PARAM_SIZE_FLG == false ){
PARAM_SIZE_FLG = true;
await Task.Run( async ()=>{
for (int ex=6;ex<80;ex++){
PARAM_SIZE=ex;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
await Task.Delay(15);
}
// 表示を消す
PARAM_SIZE=999;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
});
PARAM_SIZE_FLG = false;
}
}
// 星数,速度を画面に出す
void ParamSfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
SKPaint A_LINE= new SKPaint{
Style = SKPaintStyle.StrokeAndFill,
Color = new SKColor(255,0, 0,255),
IsAntialias=false,
TextSize=PARAM_SIZE,
TextAlign=SKTextAlign.Center,
StrokeWidth = 1
};
canvas.Clear();
if(OP_FLG) {
canvas.DrawText("STAR FIELD",(float)this.Width /2,(float)this.Height /2 ,A_LINE);
}
else{
canvas.DrawText("STAR " +STAR.ToString()+",WAIT "+ WAIT.ToString(),(float)this.Width /2,(float)this.Height /2 ,A_LINE);
}
if (PARAM_SIZE ==999 ) canvas.Clear();
}
// 星描画
void SfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
SKPaint A_LINE= new SKPaint{
Style = SKPaintStyle.StrokeAndFill,
IsAntialias=true,
StrokeWidth = 1
};
canvas.Clear(SKColors.Black);
float X, Y;
// 初めに表示する星の大きさ
float R1=No1_DEFAULT_SIZE;
float R2=No2_DEFAULT_SIZE;
float R3=No3_DEFAULT_SIZE;
float R4=No4_DEFAULT_SIZE;
float L=CENTER_SPACE;
string ANGLE_TXT = "";
char LAST_INT=(char)'0';
for (int tt = 0; tt < FIELD_CNT; tt++) {
for (int h=0;h<STAR;h++){
// マウス追従
if( MOUSE_FLG ) {
X=(float)(MX + (tt+L)* Math.Sin (STAR_ANGLE[tt,h] * Math.PI/180) );
Y=(float)(MY - (tt+L)* Math.Cos (STAR_ANGLE[tt,h] * Math.PI/180) );
}
// 表示を画面中心固定にする
else {
X=(float)(this.Width /2 + (tt+L)* Math.Sin (STAR_ANGLE[tt,h] * Math.PI/180) );
Y=(float)(this.Height/2 - (tt+L)* Math.Cos (STAR_ANGLE[tt,h] * Math.PI/180) );
}
// 角度の一桁目 -> xx (一桁目によって表示する色を変える)
ANGLE_TXT=STAR_ANGLE[tt,h].ToString();
if (ANGLE_TXT.Length==3) LAST_INT=ANGLE_TXT[2];
if (ANGLE_TXT.Length==2) LAST_INT=ANGLE_TXT[1];
if (ANGLE_TXT.Length==1) LAST_INT=ANGLE_TXT[0];
if (LAST_INT=='0') {
A_LINE.Color = new SKColor(255,127, 80,155);
canvas.DrawCircle (X,Y, R1,A_LINE);
}
if (LAST_INT == '1' || LAST_INT == '2' || LAST_INT == '3') {
A_LINE.Color = new SKColor(255,255, 255,230);
canvas.DrawCircle (X,Y, R2, A_LINE);
}
if ( LAST_INT == '4' || LAST_INT=='5' || LAST_INT == '6' || LAST_INT == '7' ) {
A_LINE.Color = new SKColor(125,125, 125,255);
canvas.DrawCircle (X,Y, R3,A_LINE);
}
if (LAST_INT =='8'|| LAST_INT == '9' ) {
A_LINE.Color = new SKColor(135,206, 235,185);
canvas.DrawCircle (X,Y, R4,A_LINE);
}
}
// 次フィールドの拡大率
L *= FIELD_RATE;
// 拡大
R1 *= No1_ZOOM_RATE;
R2 *= No2_ZOOM_RATE;
R3 *= No3_ZOOM_RATE;
R4 *= No4_ZOOM_RATE;
}
}
}
}
5.WPF (C#) 逆方向版(後から前へ)
////////////////////////////////////////
// STARFIELD (Revers) WPF+SkiaSharp
// (c)inf102 2024.
// PageUp/Down, CursolUp/Down
////////////////////////////////////////
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace SF {
public partial class MainWindow : Window {
// 速度、星数の表示
static int PARAM_SIZE=0;
static bool PARAM_SIZE_FLG; // 設定値表示中に変更キーを押した際のキャンセル用
static bool OP_FLG; // OP_Message表示フラグ
// マウス制御
static bool MOUSE_FLG;
// マウス位置
static int MX,MY;
// 描画を始めてから収束するまでの繰返しのカウンタ FIELD_MAXまでaddされる [0固定]
static int FIELD_CNT=0;
////////////////// [ ]内は設定参考値 /////////////////////////
// 1フィードに表示する最大星数。STARより大きい必要がある。起動中にSTARを可変させるので最大値を最初に設定する必要がある。
const int STAR_MAX = 30; // [30]
static int STAR=1; // 1フィールド中の星描画数 1~STAR_MAX [2]
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int WAIT=35; // [35]
const int CENTER_SPACE = 6; // 星が収束する中央エリアの広さ [7]
const int FIELD_MAX = 83; // 最大広がりエリア。画面端に星が来ない場合は大きくする。
// 中心部を画面中央にして70で画面いっぱいに表示されたとしても表示位置を端にすると反対には表示
// されない事がある。その場合も数値を上げるが大きすぎると余計な計算が増える。 [83]
// FIELD_RATEを下げるとFIELD_MAXを上げる必要がある
const float FIELD_RATE =1.080f; // 拡大割合。WAIT後にどれだけ接近するか。大きいとカクカクになる。 [1.080f]
static int[,] STAR_ANGLE = new int[FIELD_MAX, STAR_MAX]; // 星が移動する角度
// 最初に表示する星の大きさ
const float No1_DEFAULT_SIZE = 1.42f; // [1.412f]
const float No2_DEFAULT_SIZE = 1.05f; // [1.05f]
const float No3_DEFAULT_SIZE = 1.74f; // [1.74f]
const float No4_DEFAULT_SIZE = 2.01f; // [2.25f]
// 星の拡大比率
const float No1_ZOOM_RATE = 1.0033f; // [1.0033f]
const float No2_ZOOM_RATE = 1.0299f; // [1.0299f]
const float No3_ZOOM_RATE = 1.0069f; // [1.0069f]
const float No4_ZOOM_RATE = 1.0033f; // [1.0085f]
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public MainWindow() {
InitializeComponent();
}
// 配列データシフト
// 後ろからコピーして行けばbk[]は不要だった
public void DataShift(){
int []bk1 = new int [STAR_MAX];
int []bk2 = new int [STAR_MAX];
for ( int x=0;x<STAR;x++ ) bk1[x]=STAR_ANGLE[0,x];
int flg=0;
for ( int x=1;x<FIELD_MAX;x++ ){
if ( flg == 0 ){
for ( int tm=0; tm<STAR; tm++ ){
bk2[tm] = STAR_ANGLE[x,tm];
STAR_ANGLE[x,tm] = bk1[tm];
}
flg=1;
continue;
}
for ( int tm=0; tm<STAR; tm++ ){
bk1[tm] = STAR_ANGLE[x,tm];
STAR_ANGLE[x,tm] = bk2[tm];
}
flg=0;
}
}
private async void Window_Loaded(object sender, RoutedEventArgs e) {
_= Task.Run(() => SfMain());
// OP Meesage
OP_FLG = true;
await Task.Run( async ()=>{
for (int ex=250;ex>6;ex--){
//ex--;
PARAM_SIZE=ex;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
await Task.Delay(5);
}
// 表示を消す
PARAM_SIZE=999;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
});
OP_FLG = false;
}
// MAIN-LOOP
void SfMain(){
Random rnd = new Random();
while (true){
// 星が移動する方向(角度)設定 (新たな星の生成)
for ( int h=0;h<STAR;h++ ){
STAR_ANGLE[0,h]=rnd.Next(0, 360);
}
// 星表示
this.Dispatcher.Invoke ((Action)(() =>{
Sf.InvalidateVisual();
}));
// 星の配列データをシフト
DataShift();
// 何処まで展開するか
FIELD_CNT++;
if (FIELD_CNT >= FIELD_MAX) FIELD_CNT=FIELD_MAX;
Thread.Sleep(WAIT);
}
}
// マウスの位置を星の中心位置にする
private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) {
System.Windows.Point point = e.GetPosition(this);
MX=(int)point.X;
MY=(int)point.Y;
}
// マウス左ボタン アプリシャットダウン
private void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
Application.Current.Shutdown();
}
// マウス右ボタン 表示位置をマウスに追従させるか
private void Window_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
MOUSE_FLG=!MOUSE_FLG;
}
// 1フィールド中の星数可変カーソル↑↓ , 速度可変 PageUp,Dw
private async void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) {
if ( (!(e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
&& !(e.Key == System.Windows.Input.Key.PageUp || e.Key == System.Windows.Input.Key.PageDown)) ) return;
// 星数
if ( e.Key == System.Windows.Input.Key.Up && (STAR < STAR_MAX) ) STAR++;
if ( e.Key == System.Windows.Input.Key.Down && (STAR >= 2) ) STAR--;
// 速度
if ( e.Key == System.Windows.Input.Key.PageUp ) WAIT-=3;
if ( WAIT <=0 ) WAIT=2;
if ( e.Key == System.Windows.Input.Key.PageDown ) WAIT+=3;
// 設定値を画面に出す
if (PARAM_SIZE_FLG == false ){
PARAM_SIZE_FLG = true;
await Task.Run( async ()=>{
for (int ex=140;ex>2;ex--){
PARAM_SIZE=ex;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
await Task.Delay(15);
}
// 表示を消す
PARAM_SIZE=999;
this.Dispatcher.Invoke((Action)(() =>{
Param.InvalidateVisual();
}));
});
PARAM_SIZE_FLG = false;
}
}
// 星数,速度を画面に出す
void ParamSfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
SKPaint A_LINE= new SKPaint{
Style = SKPaintStyle.StrokeAndFill,
Color = new SKColor(255,0, 0,255),
IsAntialias=false,
TextSize=PARAM_SIZE,
TextAlign=SKTextAlign.Center,
StrokeWidth = 1
};
canvas.Clear();
// OP
if ( OP_FLG ) {
A_LINE.Color= new SKColor(0,255,0);
canvas.DrawText("STAR FIELD Rev.",(float)this.Width /2,(float)this.Height /2 ,A_LINE);
}
else{
canvas.DrawText("STAR " +STAR.ToString()+",WAIT "+ WAIT.ToString(),(float)this.Width /2,(float)this.Height /2 ,A_LINE);
}
if (PARAM_SIZE ==999 ) canvas.Clear();
}
// 星描画
void SfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
SKPaint A_LINE= new SKPaint{
Style = SKPaintStyle.StrokeAndFill,
IsAntialias=true,
StrokeWidth = 1
};
canvas.Clear(SKColors.Black);
float X, Y;
// 初めに表示する星の大きさ
float R1=No1_DEFAULT_SIZE;
float R2=No2_DEFAULT_SIZE;
float R3=No3_DEFAULT_SIZE;
float R4=No4_DEFAULT_SIZE;
float L=5;
string ANGLE_TXT = "";
char LAST_INT=(char)'0';
for (int tt = FIELD_MAX-1; tt > 00; tt--) { //68
for (int h=0;h<STAR;h++){
// マウス追従
if( MOUSE_FLG ) {
X=(float)(MX + (L+CENTER_SPACE)* Math.Sin (STAR_ANGLE[tt,h]*3.14/180) );
Y=(float)(MY - (L+CENTER_SPACE)* Math.Cos (STAR_ANGLE[tt,h]*3.14/180) );
}
else{
X=(float)(this.Width /2 + (L+CENTER_SPACE)* Math.Sin (STAR_ANGLE[tt,h]*3.14/180) );
Y=(float)(this.Height/2 - (L+CENTER_SPACE)* Math.Cos (STAR_ANGLE[tt,h]*3.14/180) );
}
// 角度の一桁目 -> xx (一桁目によって表示する色を変える)
ANGLE_TXT=STAR_ANGLE[tt,h].ToString();
if (ANGLE_TXT.Length==3) LAST_INT=ANGLE_TXT[2];
if (ANGLE_TXT.Length==2) LAST_INT=ANGLE_TXT[1];
if (ANGLE_TXT.Length==1) LAST_INT=ANGLE_TXT[0];
if (LAST_INT=='0') {
A_LINE.Color = new SKColor(255,127, 80,155);
canvas.DrawCircle (X,Y, R1,A_LINE);
}
if (LAST_INT == '1' || LAST_INT == '2' || LAST_INT == '3') {
A_LINE.Color = new SKColor(255,255, 255,230);
canvas.DrawCircle (X,Y, R2, A_LINE);
}
if ( LAST_INT == '4' || LAST_INT=='5' || LAST_INT == '6' || LAST_INT == '7' ) {
A_LINE.Color = new SKColor(125,125, 125,255);
canvas.DrawCircle (X,Y, R3,A_LINE);
}
if (LAST_INT =='8'|| LAST_INT == '9' ) {
A_LINE.Color = new SKColor(135,206, 235,185);
canvas.DrawCircle (X,Y, R4,A_LINE);
}
}
// フィールドの拡大率
L *= FIELD_RATE;
// 縮小率
R1 *= No1_ZOOM_RATE;
R2 *= No2_ZOOM_RATE;
R3 *= No3_ZOOM_RATE;
R4 *= No4_ZOOM_RATE;
}
}
}
}
6.WPF (共通XAML)
<Window x:Class="SF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"
xmlns:local="clr-namespace:SF" mc:Ignorable="d" WindowStyle="None"
Title="StatField" Height="200" Width="200" Loaded="Window_Loaded"
WindowState="Maximized" MouseMove="Window_MouseMove" PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown" PreviewMouseRightButtonDown="Window_PreviewMouseRightButtonDown" KeyDown="Window_KeyDown" >
<Grid>
<wpf:SKElement x:Name="Sf" PaintSurface="SfPaintSurface" />
<wpf:SKElement x:Name="Param" PaintSurface="ParamSfPaintSurface" />
</Grid>
</Window>
7.参考 (所有する初代98のメインボード CLK5MHz,MEM 640KBytes)
・上、V30に換装したMPU 下、浮動小数数値演算コ・プロセッサ i8087
i8087は現在の物価換算で20万円以上。その為、別売りとなっていた。
MEMも標準128KBytesから計640KBytesに拡張済。
・上部白いものがNEC μPD7220 グラフィック・ディスプレイ・コントローラ(GDC)
GDC https://www.richis-lab.de/GraKa02.htm