This commit is contained in:
Nikita Tsukanov 2017-04-25 15:04:12 +03:00 коммит произвёл Max Katz
Родитель c6e77e1a3e
Коммит 6ef4a7bf5c
35 изменённых файлов: 1912 добавлений и 0 удалений

63
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

1048
Annotations.cs Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

6
App.config Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>

6
App.xaml Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

27
App.xaml.cs Normal file
Просмотреть файл

@ -0,0 +1,27 @@
using Avalonia;
using Avalonia.Markup.Xaml;
using Avalonia.BattleCity.Model;
namespace Avalonia.BattleCity
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
static void Main(string[] args)
{
AppBuilder.Configure<App>()
.UsePlatformDetect()
.Start<MainWindow>(() =>
{
var field = new GameField();
var game = new Game(field);
game.Start();
return field;
});
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp1.1;net461</TargetFrameworks>
<AssemblyName>Avalonia.BattleCity</AssemblyName>
<OutputType>exe</OutputType>
<PackageId>Avalonia.BattleCity</PackageId>
<RuntimeIdentifiers>win7-x64;ubuntu.14.04-x64;osx.10.10-x64;win7-x86</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<OutputTypeEx>winexe</OutputTypeEx>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="**\*.xaml;Assets\*;Resources\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.4.1-*" />
<PackageReference Include="Avalonia.Skia.Desktop" Version="0.4.1-*" Condition="'$(TargetFramework)' != 'net461'" />
<PackageReference Include="Avalonia.Direct2D1" Version="0.4.1-*" Condition="'$(TargetFramework)' == 'net461'" />
<PackageReference Include="Avalonia.Win32" Version="0.4.1-*" />
<PackageReference Include="Avalonia.Gtk3" Version="0.4.1-*" />
</ItemGroup>
</Project>

22
Avalonia.BattleCity.sln Normal file
Просмотреть файл

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.BattleCity", "Avalonia.BattleCity.csproj", "{1CDCB582-4777-49F0-A8B5-F4D62C9BB4A0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1CDCB582-4777-49F0-A8B5-F4D62C9BB4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CDCB582-4777-49F0-A8B5-F4D62C9BB4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CDCB582-4777-49F0-A8B5-F4D62C9BB4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CDCB582-4777-49F0-A8B5-F4D62C9BB4A0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,21 @@
using System;
using System.Globalization;
using Avalonia.Markup;
using Avalonia.BattleCity.Model;
namespace Avalonia.BattleCity.Infrastructure
{
public class CellToScreenConverter : IValueConverter
{
public static CellToScreenConverter Instance { get; } = new CellToScreenConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToDouble(value)*GameField.CellSize;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Markup;
using Avalonia.Media;
using Avalonia.BattleCity.Model;
namespace Avalonia.BattleCity.Infrastructure
{
class DirectionToMatrixConverter : IValueConverter
{
public static DirectionToMatrixConverter Instance { get; } = new DirectionToMatrixConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var direction = (Facing) value;
var matrix = Matrix.Identity;
if (direction == Facing.South)
{
matrix = Matrix.CreateScale(1, -1);
}
if (direction == Facing.East)
{
matrix = Matrix.CreateRotation(1.5708);
}
if (direction == Facing.West)
{
matrix = Matrix.CreateRotation(1.5708) * Matrix.CreateScale(-1, 1);
}
return new MatrixTransform(matrix);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Avalonia.BattleCity.Infrastructure
{
public abstract class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Avalonia.Markup;
using Avalonia.Media.Imaging;
using Avalonia.BattleCity.Model;
namespace Avalonia.BattleCity.Infrastructure
{
public class TerrainTileConverter : IValueConverter
{
public static TerrainTileConverter Instance { get; } = new TerrainTileConverter();
private static Dictionary<TerrainTileType, Bitmap> _cache;
Dictionary<TerrainTileType, Bitmap> GetCache()
{
return
_cache ??
(_cache = Enum.GetValues(typeof(TerrainTileType)).OfType<TerrainTileType>().ToDictionary(t => t, t =>
new Bitmap(
typeof(TerrainTileConverter).GetTypeInfo()
.Assembly.GetManifestResourceStream($"Avalonia.BattleCity.Resources.{t}.png"))));
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => GetCache()[(TerrainTileType) value];
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia;
using System.Globalization;
using Avalonia.Markup;
using Avalonia.Media;
using Avalonia.BattleCity.Model;
namespace Avalonia.BattleCity.Infrastructure
{
class ZIndexConverter : IValueConverter
{
public static ZIndexConverter Instance { get; } = new ZIndexConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Player || value is Tank)
return 1;
else return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

14
Keyboard.cs Normal file
Просмотреть файл

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Input;
namespace Avalonia.BattleCity
{
static class Keyboard
{
public static readonly HashSet<Key> Keys = new HashSet<Key>();
public static bool IsKeyDown(Key key) => Keys.Contains(key);
}
}

40
MainWindow.xaml Normal file
Просмотреть файл

@ -0,0 +1,40 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:local="clr-namespace:Avalonia.BattleCity;assembly=Avalonia.BattleCity"
xmlns:model="clr-namespace:Avalonia.BattleCity.Model;assembly=Avalonia.BattleCity"
xmlns:infrastructure="clr-namespace:Avalonia.BattleCity.Infrastructure;assembly=Avalonia.BattleCity"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Avalonia.BattleCity" Width="640" Height="480" Design.DataContext="{Static model:GameField.DesignInstance}" >
<Window.Styles >
<Style Selector="ItemsControl > ContentPresenter">
<Setter Property="ZIndex" Value="{Binding Converter={Static infrastructure:ZIndexConverter.Instance}, Path=.}"/>
<Setter Property="Canvas.Left" Value="{Binding Location.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Location.Y}"/>
</Style>
</Window.Styles>
<ItemsControl
Items="{Binding GameObjects}"
Width="{Binding Width, Converter={Static infrastructure:CellToScreenConverter.Instance}, Mode=OneWay}"
Height="{Binding Height, Converter={Static infrastructure:CellToScreenConverter.Instance}, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate DataType="{x:Type model:TerrainTile}">
<Image Width="32" Height="32"
Source="{Binding Type, Converter={Static infrastructure:TerrainTileConverter.Instance}}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:Player}">
<Image Width="32" Height="32" Source="resm:Avalonia.BattleCity.Resources.Player.png" RenderTransform="{Binding Facing, Converter={Static infrastructure:DirectionToMatrixConverter.Instance}}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:Tank}">
<Image Width="32" Height="32" Source="resm:Avalonia.BattleCity.Resources.Tank.png" RenderTransform="{Binding Facing, Converter={Static infrastructure:DirectionToMatrixConverter.Instance}}"/>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
</Window>

31
MainWindow.xaml.cs Normal file
Просмотреть файл

@ -0,0 +1,31 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
namespace Avalonia.BattleCity
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
AvaloniaXamlLoader.Load(this);
this.AttachDevTools();
}
protected override void OnKeyDown(KeyEventArgs e)
{
Keyboard.Keys.Add(e.Key);
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
Keyboard.Keys.Remove(e.Key);
base.OnKeyUp(e);
}
}
}

72
Model/Actor.cs Normal file
Просмотреть файл

@ -0,0 +1,72 @@
using System.Collections.Generic;
using Avalonia;
using Avalonia.BattleCity.Infrastructure;
namespace Avalonia.BattleCity.Model
{
public abstract class GameObject : PropertyChangedBase
{
private Point _location;
public Point Location
{
get { return _location; }
protected set
{
if (value.Equals(_location)) return;
_location = value;
OnPropertyChanged();
}
}
public virtual int Layer => 0;
protected GameObject(Point location)
{
Location = location;
}
}
public enum TerrainTileType
{
Plain, //passable, shoot-thru
WoodWall, //impassable, takes 1 shot to bring down
StoneWall, //impassable, indestructible
Water, //impassable, shoot-thru
Pavement, //passable, 2x speed
Forest //passable at half speed, shoot-thru
}
public class TerrainTile : GameObject
{
private static readonly Dictionary<TerrainTileType, double> Speeds = new Dictionary<TerrainTileType, double>
{
{TerrainTileType.Plain, 1},
{TerrainTileType.WoodWall, 0},
{TerrainTileType.StoneWall, 0},
{TerrainTileType.Water, 0},
{TerrainTileType.Pavement, 2},
{TerrainTileType.Forest, 0.5}
};
private static readonly Dictionary<TerrainTileType, bool> ShootThrus = new Dictionary<TerrainTileType, bool>
{
{TerrainTileType.Plain, true},
{TerrainTileType.WoodWall, false},
{TerrainTileType.StoneWall, false},
{TerrainTileType.Water, true},
{TerrainTileType.Pavement, true},
{TerrainTileType.Forest, true},
};
public double Speed => Speeds[Type];
public bool ShootThru => ShootThrus[Type];
public bool IsPassable => Speed > 0.1;
public TerrainTileType Type { get; set; }
public TerrainTile(Point location, TerrainTileType type) : base(location)
{
Type = type;
}
}
}

11
Model/Apple.cs Normal file
Просмотреть файл

@ -0,0 +1,11 @@
using Avalonia;
namespace Avalonia.BattleCity.Model
{
public class Apple : GameObject
{
public Apple(Point location) : base(location)
{
}
}
}

46
Model/CellLocation.cs Normal file
Просмотреть файл

@ -0,0 +1,46 @@
using Avalonia;
namespace Avalonia.BattleCity.Model
{
public struct CellLocation
{
public bool Equals(CellLocation other)
{
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is CellLocation && Equals((CellLocation) obj);
}
public static bool operator ==(CellLocation l1, CellLocation l2) => l1.Equals(l2);
public static bool operator !=(CellLocation l1, CellLocation l2) => !(l1 == l2);
public override int GetHashCode()
{
unchecked
{
return (X*397) ^ Y;
}
}
public override string ToString() => $"({X}:{Y})";
public Point ToPoint() => new Point(GameField.CellSize*X, GameField.CellSize*Y);
public CellLocation(int x, int y)
{
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
public CellLocation WithX(int x) => new CellLocation(x, Y);
public CellLocation WithY(int y) => new CellLocation(X, y);
}
}

10
Model/Facing.cs Normal file
Просмотреть файл

@ -0,0 +1,10 @@
namespace Avalonia.BattleCity.Model
{
public enum Facing
{
North,
East,
South,
West
}
}

42
Model/Game.cs Normal file
Просмотреть файл

@ -0,0 +1,42 @@
using System;
using System.Linq;
using Avalonia.Input;
namespace Avalonia.BattleCity.Model
{
public class Game : GameBase
{
private readonly GameField _field;
public Game(GameField field)
{
_field = field;
}
private Random Random { get; } = new Random();
protected override void Tick()
{
if (!_field.Player.IsMoving)
{
if (Keyboard.IsKeyDown(Key.Up))
_field.Player.SetTarget(Facing.North);
else if (Keyboard.IsKeyDown(Key.Down))
_field.Player.SetTarget(Facing.South);
else if (Keyboard.IsKeyDown(Key.Left))
_field.Player.SetTarget(Facing.West);
else if (Keyboard.IsKeyDown(Key.Right))
_field.Player.SetTarget(Facing.East);
}
foreach (var tank in _field.GameObjects.OfType<Tank>())
if (!tank.IsMoving)
{
if (!tank.SetTarget(tank.Facing))
tank.SetTarget((Facing) Random.Next(4));
}
foreach(var obj in _field.GameObjects.OfType<MovingGameObject>())
obj.MoveToTarget();
}
}
}

29
Model/GameBase.cs Normal file
Просмотреть файл

@ -0,0 +1,29 @@
using System;
using Avalonia.Threading;
namespace Avalonia.BattleCity.Model
{
public abstract class GameBase
{
public const int TicksPerSecond = 60;
public long CurrentTick { get; private set; }
private readonly DispatcherTimer _timer = new DispatcherTimer() {Interval = new TimeSpan(0, 0, 0, 0, 1000/TicksPerSecond)};
void DoTick()
{
Tick();
CurrentTick++;
}
protected abstract void Tick();
protected GameBase()
{
_timer.Tick += delegate { DoTick(); };
}
public void Start() => _timer.IsEnabled = true;
public void Stop() => _timer.IsEnabled = false;
}
}

78
Model/GameField.cs Normal file
Просмотреть файл

@ -0,0 +1,78 @@
using System;
using System.Collections.ObjectModel;
using Avalonia;
using Avalonia.BattleCity.Infrastructure;
namespace Avalonia.BattleCity.Model
{
public class GameField : PropertyChangedBase
{
public static GameField DesignInstance { get; } = new GameField();
public const double CellSize = 32;
public ObservableCollection<GameObject> GameObjects { get; } = new ObservableCollection<GameObject>();
public TerrainTile[,] Tiles { get; }
public Player Player { get; }
public int Height { get; }
public int Width{ get; }
public GameField() : this(20, 15)
{
}
Random Random { get; } = new Random();
TerrainTileType GetTypeForCoords(int x, int y)
{
if (x / 2 == Width / 4)
return TerrainTileType.Pavement;
if (y/2 == Height/4)
{
return TerrainTileType.Water;
}
if (x*y == 0) return TerrainTileType.StoneWall;
if((x+1-Width)*(y+1-Height) == 0) return TerrainTileType.WoodWall;
//if(Random.NextDouble()<0.1) return TerrainTileType.WoodWall;
if(Random.NextDouble()<0.3) return TerrainTileType.Forest;
return TerrainTileType.Plain;
}
public GameField(int width, int height)
{
Width = width;
Height = height;
Tiles = new TerrainTile[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
GameObjects.Add(
Tiles[x, y] =
new TerrainTile(new Point(x*CellSize, y*CellSize), GetTypeForCoords(x,y)));
}
}
GameObjects.Add(
Player = new Player(this, new CellLocation(width/2, height/2), Facing.North));
for (var c = 0; c < 10;)
{
var x = Random.Next(Width - 1);
var y = Random.Next(Height - 1);
if (!Tiles[x, y].IsPassable)
continue;
c++;
GameObjects.Add(new Tank(this, new CellLocation(x, y), (Facing) Random.Next(4),
Random.NextDouble()*4 + 1));
}
}
}
}

156
Model/MovingActor.cs Normal file
Просмотреть файл

@ -0,0 +1,156 @@
using System;
using System.Linq;
namespace Avalonia.BattleCity.Model
{
public abstract class MovingGameObject : GameObject
{
private readonly GameField _field;
private Facing _facing;
private CellLocation _cellLocation;
private CellLocation _targetCellLocation;
public override int Layer => 1;
public Facing Facing
{
get { return _facing; }
set
{
if (value == _facing) return;
_facing = value;
OnPropertyChanged();
}
}
public CellLocation CellLocation
{
get { return _cellLocation; }
private set
{
if (value.Equals(_cellLocation)) return;
_cellLocation = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsMoving));
}
}
public CellLocation TargetCellLocation
{
get { return _targetCellLocation; }
private set
{
if (value.Equals(_targetCellLocation)) return;
_targetCellLocation = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsMoving));
}
}
protected MovingGameObject(GameField field, CellLocation location, Facing facing) : base(location.ToPoint())
{
_field = field;
Facing = facing;
CellLocation = TargetCellLocation = location;
}
public bool IsMoving => TargetCellLocation != CellLocation;
public bool SetTarget(CellLocation loc)
{
if (IsMoving)
//We are the bear rolling from the hill
throw new InvalidOperationException("Unable to change direction while moving");
if(loc == CellLocation)
return true;
Facing = GetDirection(CellLocation, loc);
if (loc.X < 0 || loc.Y < 0)
return false;
if (loc.X >= _field.Width || loc.Y >= _field.Height)
return false;
if (!_field.Tiles[loc.X, loc.Y].IsPassable)
return false;
if (
_field.GameObjects.OfType<MovingGameObject>()
.Any(t => t != this && (t.CellLocation == loc || t.TargetCellLocation == loc)))
return false;
TargetCellLocation = loc;
return true;
}
public CellLocation GetTileAtDirection(Facing facing)
{
if (facing == Facing.North)
return CellLocation.WithY(CellLocation.Y - 1);
if (facing == Facing.South)
return CellLocation.WithY(CellLocation.Y + 1);
if (facing == Facing.West)
return CellLocation.WithX(CellLocation.X - 1);
return CellLocation.WithX(CellLocation.X + 1);
}
public bool SetTarget(Facing facing) => SetTarget(GetTileAtDirection(facing));
Facing GetDirection(CellLocation current, CellLocation target)
{
if (target.X < current.X)
return Facing.West;
if (target.X > current.X)
return Facing.East;
if (target.Y < current.Y)
return Facing.North;
return Facing.South;
}
public void SetLocation(CellLocation loc)
{
CellLocation = loc;
Location = loc.ToPoint();
}
protected virtual double SpeedFactor => (double)1/15;
public void MoveToTarget()
{
if (TargetCellLocation == CellLocation)
return;
var speed = GameField.CellSize*
(_field.Tiles[CellLocation.X, CellLocation.Y].Speed +
_field.Tiles[TargetCellLocation.X, TargetCellLocation.Y].Speed)/2
*SpeedFactor;
var pos = Location;
var direction = GetDirection(CellLocation, TargetCellLocation);
if (direction == Facing.North)
{
pos = pos.WithY(pos.Y - speed);
Location = pos;
if (pos.Y/GameField.CellSize <= TargetCellLocation.Y)
SetLocation(TargetCellLocation);
}
else if (direction == Facing.South)
{
pos = pos.WithY(pos.Y + speed);
Location = pos;
if (pos.Y / GameField.CellSize >= TargetCellLocation.Y)
SetLocation(TargetCellLocation);
}
else if (direction == Facing.West)
{
pos = pos.WithX(pos.X - speed);
Location = pos;
if (pos.X / GameField.CellSize <= TargetCellLocation.X)
SetLocation(TargetCellLocation);
}
else if (direction == Facing.East)
{
pos = pos.WithX(pos.X + speed);
Location = pos;
if (pos.X / GameField.CellSize >= TargetCellLocation.X)
SetLocation(TargetCellLocation);
}
}
}
}

9
Model/Player.cs Normal file
Просмотреть файл

@ -0,0 +1,9 @@
namespace Avalonia.BattleCity.Model
{
public class Player : MovingGameObject
{
public Player(GameField field, CellLocation location, Facing facing) : base(field, location, facing)
{
}
}
}

14
Model/Tank.cs Normal file
Просмотреть файл

@ -0,0 +1,14 @@
namespace Avalonia.BattleCity.Model
{
public class Tank : MovingGameObject
{
private readonly double _speed;
protected override double SpeedFactor => _speed*base.SpeedFactor;
public Tank(GameField field, CellLocation location, Facing facing, double speed) : base(field, location, facing)
{
_speed = speed;
}
}
}

14
NuGet.Config Normal file
Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageRestore>
<add key="enabled" value="True" />
<add key="automatic" value="True" />
</packageRestore>
<packageSources>
<add key="NuGet 3 (plain)" value="http://api.nuget.org/v3/index.json" />
<add key="Avalonia Nightly" value="https://www.myget.org/F/avalonia-ci/api/v2" />
</packageSources>
<bindingRedirects>
<add key="skip" value="False" />
</bindingRedirects>
</configuration>

14
README.md Normal file
Просмотреть файл

@ -0,0 +1,14 @@
# Avalonia.BattleCity
Port of https://github.com/hacklex/PekaCity to Avalonia.
2D game stub rendered completely by AvaloniaUI
##What is this? Why?
Well, this is a stub for a 2D game. The purpose of the project was to demonstrate that one can write a 2D game in AvaloniaUI without writing any rendering code.
## Features
- 2D Tiles. Not yet animated, but animating won't be a problem, I guess
- Cell-aligned game objects
- No rendering code, everything is done using AvaloniaUI data binding and a few ValueConverters

Двоичные данные
Resources/Forest.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичные данные
Resources/Pavement.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Двоичные данные
Resources/Plain.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичные данные
Resources/Player.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
Resources/StoneWall.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 615 B

Двоичные данные
Resources/Tank.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 812 B

Двоичные данные
Resources/Water.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Двоичные данные
Resources/WoodWall.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB